paperlint 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +72 -0
- package/.github/workflows/ci.yml +297 -0
- package/.github/workflows/dependabot-automerge.yml +70 -0
- package/.github/workflows/pr-title.yml +59 -0
- package/.github/workflows/release.yml +54 -0
- package/CLAUDE.md +598 -0
- package/CONTRIBUTING.md +159 -0
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/action.harness.mjs +287 -0
- package/action.mutations.mjs +162 -0
- package/action.yml +138 -0
- package/bin/rpp.mjs +43 -0
- package/dist/action-ref.d.ts +12 -0
- package/dist/action-ref.d.ts.map +1 -0
- package/dist/action-ref.js +16 -0
- package/dist/action-ref.js.map +1 -0
- package/dist/adapters/banal/failure.d.ts +73 -0
- package/dist/adapters/banal/failure.d.ts.map +1 -0
- package/dist/adapters/banal/failure.js +58 -0
- package/dist/adapters/banal/failure.js.map +1 -0
- package/dist/adapters/banal/index.d.ts +17 -0
- package/dist/adapters/banal/index.d.ts.map +1 -0
- package/dist/adapters/banal/index.js +56 -0
- package/dist/adapters/banal/index.js.map +1 -0
- package/dist/adapters/banal/install.d.ts +26 -0
- package/dist/adapters/banal/install.d.ts.map +1 -0
- package/dist/adapters/banal/install.js +15 -0
- package/dist/adapters/banal/install.js.map +1 -0
- package/dist/adapters/banal/invocation.d.ts +48 -0
- package/dist/adapters/banal/invocation.d.ts.map +1 -0
- package/dist/adapters/banal/invocation.js +43 -0
- package/dist/adapters/banal/invocation.js.map +1 -0
- package/dist/adapters/banal/locate.d.ts +50 -0
- package/dist/adapters/banal/locate.d.ts.map +1 -0
- package/dist/adapters/banal/locate.js +34 -0
- package/dist/adapters/banal/locate.js.map +1 -0
- package/dist/adapters/banal/output.d.ts +27 -0
- package/dist/adapters/banal/output.d.ts.map +1 -0
- package/dist/adapters/banal/output.js +112 -0
- package/dist/adapters/banal/output.js.map +1 -0
- package/dist/adapters/banal/pin.d.ts +19 -0
- package/dist/adapters/banal/pin.d.ts.map +1 -0
- package/dist/adapters/banal/pin.js +15 -0
- package/dist/adapters/banal/pin.js.map +1 -0
- package/dist/adapters/banal/probe.d.ts +12 -0
- package/dist/adapters/banal/probe.d.ts.map +1 -0
- package/dist/adapters/banal/probe.js +27 -0
- package/dist/adapters/banal/probe.js.map +1 -0
- package/dist/adapters/banal/run.d.ts +89 -0
- package/dist/adapters/banal/run.d.ts.map +1 -0
- package/dist/adapters/banal/run.js +104 -0
- package/dist/adapters/banal/run.js.map +1 -0
- package/dist/adapters/banal/settings.d.ts +18 -0
- package/dist/adapters/banal/settings.d.ts.map +1 -0
- package/dist/adapters/banal/settings.js +29 -0
- package/dist/adapters/banal/settings.js.map +1 -0
- package/dist/adapters/banal/xml.d.ts +48 -0
- package/dist/adapters/banal/xml.d.ts.map +1 -0
- package/dist/adapters/banal/xml.js +67 -0
- package/dist/adapters/banal/xml.js.map +1 -0
- package/dist/adapters/curl/download.io.d.ts +14 -0
- package/dist/adapters/curl/download.io.d.ts.map +1 -0
- package/dist/adapters/curl/download.io.js +69 -0
- package/dist/adapters/curl/download.io.js.map +1 -0
- package/dist/adapters/curl/index.d.ts +6 -0
- package/dist/adapters/curl/index.d.ts.map +1 -0
- package/dist/adapters/curl/index.js +6 -0
- package/dist/adapters/curl/index.js.map +1 -0
- package/dist/adapters/memory/index.d.ts +43 -0
- package/dist/adapters/memory/index.d.ts.map +1 -0
- package/dist/adapters/memory/index.js +79 -0
- package/dist/adapters/memory/index.js.map +1 -0
- package/dist/adapters/node/files.io.d.ts +3 -0
- package/dist/adapters/node/files.io.d.ts.map +1 -0
- package/dist/adapters/node/files.io.js +31 -0
- package/dist/adapters/node/files.io.js.map +1 -0
- package/dist/adapters/node/host.io.d.ts +3 -0
- package/dist/adapters/node/host.io.d.ts.map +1 -0
- package/dist/adapters/node/host.io.js +14 -0
- package/dist/adapters/node/host.io.js.map +1 -0
- package/dist/adapters/node/index.d.ts +25 -0
- package/dist/adapters/node/index.d.ts.map +1 -0
- package/dist/adapters/node/index.js +14 -0
- package/dist/adapters/node/index.js.map +1 -0
- package/dist/adapters/node/process.io.d.ts +14 -0
- package/dist/adapters/node/process.io.d.ts.map +1 -0
- package/dist/adapters/node/process.io.js +41 -0
- package/dist/adapters/node/process.io.js.map +1 -0
- package/dist/adapters/node/workspace.io.d.ts +4 -0
- package/dist/adapters/node/workspace.io.d.ts.map +1 -0
- package/dist/adapters/node/workspace.io.js +33 -0
- package/dist/adapters/node/workspace.io.js.map +1 -0
- package/dist/adapters/pdfjs/fill.d.ts +42 -0
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -0
- package/dist/adapters/pdfjs/fill.js +91 -0
- package/dist/adapters/pdfjs/fill.js.map +1 -0
- package/dist/build-engine.d.ts +48 -0
- package/dist/build-engine.d.ts.map +1 -0
- package/dist/build-engine.js +148 -0
- package/dist/build-engine.js.map +1 -0
- package/dist/build.d.ts +163 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +575 -0
- package/dist/build.js.map +1 -0
- package/dist/cli.d.ts +151 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +951 -0
- package/dist/cli.js.map +1 -0
- package/dist/doctor.d.ts +42 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +280 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain/geometry.d.ts +71 -0
- package/dist/domain/geometry.d.ts.map +1 -0
- package/dist/domain/geometry.js +35 -0
- package/dist/domain/geometry.js.map +1 -0
- package/dist/domain/host.d.ts +16 -0
- package/dist/domain/host.d.ts.map +1 -0
- package/dist/domain/host.js +8 -0
- package/dist/domain/host.js.map +1 -0
- package/dist/domain/page-layout.d.ts +34 -0
- package/dist/domain/page-layout.d.ts.map +1 -0
- package/dist/domain/page-layout.js +8 -0
- package/dist/domain/page-layout.js.map +1 -0
- package/dist/domain/paths.d.ts +5 -0
- package/dist/domain/paths.d.ts.map +1 -0
- package/dist/domain/paths.js +2 -0
- package/dist/domain/paths.js.map +1 -0
- package/dist/domain/result.d.ts +23 -0
- package/dist/domain/result.d.ts.map +1 -0
- package/dist/domain/result.js +10 -0
- package/dist/domain/result.js.map +1 -0
- package/dist/domain/sha256.d.ts +7 -0
- package/dist/domain/sha256.d.ts.map +1 -0
- package/dist/domain/sha256.js +14 -0
- package/dist/domain/sha256.js.map +1 -0
- package/dist/domain/text.d.ts +6 -0
- package/dist/domain/text.d.ts.map +1 -0
- package/dist/domain/text.js +7 -0
- package/dist/domain/text.js.map +1 -0
- package/dist/engine.d.ts +93 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +119 -0
- package/dist/engine.js.map +1 -0
- package/dist/exit-code.d.ts +22 -0
- package/dist/exit-code.d.ts.map +1 -0
- package/dist/exit-code.js +10 -0
- package/dist/exit-code.js.map +1 -0
- package/dist/facts-file.d.ts +96 -0
- package/dist/facts-file.d.ts.map +1 -0
- package/dist/facts-file.js +134 -0
- package/dist/facts-file.js.map +1 -0
- package/dist/hooks-settings.d.ts +141 -0
- package/dist/hooks-settings.d.ts.map +1 -0
- package/dist/hooks-settings.js +306 -0
- package/dist/hooks-settings.js.map +1 -0
- package/dist/init.d.ts +201 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +579 -0
- package/dist/init.js.map +1 -0
- package/dist/latex-log.d.ts +80 -0
- package/dist/latex-log.d.ts.map +1 -0
- package/dist/latex-log.js +187 -0
- package/dist/latex-log.js.map +1 -0
- package/dist/latex-loop.d.ts +129 -0
- package/dist/latex-loop.d.ts.map +1 -0
- package/dist/latex-loop.js +113 -0
- package/dist/latex-loop.js.map +1 -0
- package/dist/link-skills.d.ts +51 -0
- package/dist/link-skills.d.ts.map +1 -0
- package/dist/link-skills.js +199 -0
- package/dist/link-skills.js.map +1 -0
- package/dist/new-paper.d.ts +48 -0
- package/dist/new-paper.d.ts.map +1 -0
- package/dist/new-paper.js +110 -0
- package/dist/new-paper.js.map +1 -0
- package/dist/pdf-facts.d.ts +44 -0
- package/dist/pdf-facts.d.ts.map +1 -0
- package/dist/pdf-facts.js +239 -0
- package/dist/pdf-facts.js.map +1 -0
- package/dist/pdf-geometry.d.ts +170 -0
- package/dist/pdf-geometry.d.ts.map +1 -0
- package/dist/pdf-geometry.js +158 -0
- package/dist/pdf-geometry.js.map +1 -0
- package/dist/ports/download.d.ts +9 -0
- package/dist/ports/download.d.ts.map +1 -0
- package/dist/ports/download.js +2 -0
- package/dist/ports/download.js.map +1 -0
- package/dist/ports/files.d.ts +11 -0
- package/dist/ports/files.d.ts.map +1 -0
- package/dist/ports/files.js +2 -0
- package/dist/ports/files.js.map +1 -0
- package/dist/ports/measure-geometry.d.ts +8 -0
- package/dist/ports/measure-geometry.d.ts.map +1 -0
- package/dist/ports/measure-geometry.js +2 -0
- package/dist/ports/measure-geometry.js.map +1 -0
- package/dist/ports/process.d.ts +45 -0
- package/dist/ports/process.d.ts.map +1 -0
- package/dist/ports/process.js +2 -0
- package/dist/ports/process.js.map +1 -0
- package/dist/ports/tool-installer.d.ts +29 -0
- package/dist/ports/tool-installer.d.ts.map +1 -0
- package/dist/ports/tool-installer.js +2 -0
- package/dist/ports/tool-installer.js.map +1 -0
- package/dist/ports/workspace.d.ts +18 -0
- package/dist/ports/workspace.d.ts.map +1 -0
- package/dist/ports/workspace.js +2 -0
- package/dist/ports/workspace.js.map +1 -0
- package/dist/rules-config.d.ts +34 -0
- package/dist/rules-config.d.ts.map +1 -0
- package/dist/rules-config.js +132 -0
- package/dist/rules-config.js.map +1 -0
- package/dist/structure.d.ts +34 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +149 -0
- package/dist/structure.js.map +1 -0
- package/dist/tex-requirements.d.ts +43 -0
- package/dist/tex-requirements.d.ts.map +1 -0
- package/dist/tex-requirements.js +127 -0
- package/dist/tex-requirements.js.map +1 -0
- package/dist/toolchain.d.ts +159 -0
- package/dist/toolchain.d.ts.map +1 -0
- package/dist/toolchain.js +542 -0
- package/dist/toolchain.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/configuration.md +235 -0
- package/docs/e2e.md +152 -0
- package/docs/incidents.md +59 -0
- package/docs/install.md +170 -0
- package/docs/optional-rules.md +107 -0
- package/docs/package-shape-options.md +262 -0
- package/docs/prior-art/README.md +76 -0
- package/docs/prior-art/blocking-vs-advisory.md +83 -0
- package/docs/prior-art/content-delivery.md +124 -0
- package/docs/prior-art/multi-mode-tools.md +106 -0
- package/docs/prior-art/nondeterministic-checks.md +99 -0
- package/docs/prior-art/package-location.md +422 -0
- package/docs/prior-art/paper-folder-scaffolding.md +538 -0
- package/docs/prior-art/readme-structure.md +69 -0
- package/docs/prior-art/repro/README.md +92 -0
- package/docs/prior-art/repro/claim1-allowedtools.mjs +66 -0
- package/docs/prior-art/repro/claim1-at2.mjs +40 -0
- package/docs/prior-art/repro/claim1-crosschannel.mjs +54 -0
- package/docs/prior-art/repro/claim1-frontmatter.mjs +76 -0
- package/docs/prior-art/repro/claim1-hook-payload-reporter.mjs +10 -0
- package/docs/prior-art/repro/claim1-plugin-frontmatter.mjs +27 -0
- package/docs/prior-art/repro/claim1-plugin-skill.mjs +52 -0
- package/docs/prior-art/repro/claim1-project-skill.mjs +81 -0
- package/docs/prior-art/repro/claim2-marketplace-flat-asclaimed.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-negative-control.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-exact.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-noversion.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-range.json +1 -0
- package/docs/prior-art/repro/claim3-imports.mjs +50 -0
- package/docs/prior-art/repro/claim4-find-package-json.mjs +8 -0
- package/docs/prior-art/repro/claim4-package-dir.mjs +39 -0
- package/docs/prior-art/repro/claim4-parent-arg.mjs +17 -0
- package/docs/prior-art/repro/claim4-resolve-apis.mjs +21 -0
- package/docs/prior-art/repro/claim4-setup-consumers.mjs +45 -0
- package/docs/prior-art/repro/claim4-yarn-pnp.mjs +70 -0
- package/docs/prior-art/repro/claim5-bin-launch.mjs +39 -0
- package/docs/prior-art/repro/claim5-exports-mutation.mjs +57 -0
- package/docs/prior-art/repro/claim5-resolved-location-and-bin.mjs +33 -0
- package/docs/prior-art/repro/claim6-candidate-ambiguity.mjs +17 -0
- package/docs/prior-art/repro/claim6-doc-path-candidates.mjs +27 -0
- package/docs/prior-art/test-tooling.md +131 -0
- package/docs/rules.md +58 -0
- package/docs/texlive-install-decision.md +230 -0
- package/docs/toolchain.md +152 -0
- package/eslint-rules/doc-fields.harness.mjs +336 -0
- package/eslint-rules/doc-fields.mjs +186 -0
- package/eslint-rules/doc-fields.mutations.mjs +96 -0
- package/eslint-rules/install-path-literals.harness.mjs +121 -0
- package/eslint-rules/install-path-literals.mjs +108 -0
- package/eslint-rules/install-path-literals.mutations.mjs +62 -0
- package/eslint-rules/latex-language.harness.mjs +599 -0
- package/eslint-rules/latex-language.mjs +591 -0
- package/eslint-rules/latex-language.mutations.mjs +196 -0
- package/eslint-rules/paper-research-question.harness.mjs +146 -0
- package/eslint-rules/paper-research-question.mjs +180 -0
- package/eslint-rules/paper-research-question.mutations.mjs +127 -0
- package/eslint-rules/paper-stages.harness.mjs +356 -0
- package/eslint-rules/paper-stages.mjs +455 -0
- package/eslint-rules/paper-stages.mutations.mjs +157 -0
- package/eslint-rules/paper-typography.harness.mjs +291 -0
- package/eslint-rules/paper-typography.mjs +313 -0
- package/eslint-rules/paper-typography.mutations.mjs +131 -0
- package/eslint-rules/papers.harness.mjs +259 -0
- package/eslint-rules/papers.mjs +166 -0
- package/eslint-rules/papers.mutations.mjs +186 -0
- package/eslint-rules/pdf-last-page-balance.harness.mjs +206 -0
- package/eslint-rules/pdf-last-page-balance.mjs +208 -0
- package/eslint-rules/review-findings-cause.harness.mjs +228 -0
- package/eslint-rules/review-findings-cause.mjs +135 -0
- package/eslint-rules/review-findings-cause.mutations.mjs +72 -0
- package/eslint-rules/temp-root-realpath.harness.mjs +176 -0
- package/eslint-rules/temp-root-realpath.mjs +129 -0
- package/eslint-rules/temp-root-realpath.mutations.mjs +99 -0
- package/eslint-rules/tex-build.harness.mjs +753 -0
- package/eslint-rules/tex-build.mjs +322 -0
- package/eslint-rules/tex-build.mutations.mjs +258 -0
- package/eslint.config.mjs +521 -0
- package/fixtures/build-e2e/acmart/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/acmart/paper.tex +11 -0
- package/fixtures/build-e2e/acmart/venue.json +1 -0
- package/fixtures/build-e2e/broken/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/broken/paper.tex +7 -0
- package/fixtures/build-e2e/cite/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/cite/build.sh +5 -0
- package/fixtures/build-e2e/cite/paper.tex +10 -0
- package/fixtures/build-e2e/cite/refs.bib +9 -0
- package/fixtures/build-e2e/empty/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/empty/paper.tex +6 -0
- package/fixtures/build-e2e/fallback/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/fallback/paper.tex +11 -0
- package/fixtures/build-e2e/guards/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/guards/paper.tex +10 -0
- package/fixtures/build-e2e/no-source/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/paper.tex +28 -0
- package/fixtures/build-e2e/unbalanced/refs.bib +269 -0
- package/fixtures/install-path-literals/clean.fixture.mjs +3 -0
- package/fixtures/install-path-literals/clean.md +15 -0
- package/fixtures/install-path-literals/defect.fixture.mjs +3 -0
- package/fixtures/install-path-literals/defect.md +14 -0
- package/fixtures/latex-language/clean.tex +50 -0
- package/fixtures/latex-language/defect.tex +52 -0
- package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/comment-only/paper.tex +7 -0
- package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +6 -0
- package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +6 -0
- package/fixtures/paper-research-question/draft/paper.tex +2 -0
- package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/markdown-no-rq/paper.md +4 -0
- package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +12 -0
- package/fixtures/paper-research-question/shipped-no-rq/paper.tex +3 -0
- package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/shipped-with-rq/paper.tex +2 -0
- package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +16 -0
- package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +17 -0
- package/fixtures/paper-stages/nofile/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/noheader/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/noheader/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/nothing/PIPELINE-STATUS.md +3 -0
- package/fixtures/paper-stages/ok/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-stages/ok/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/stale/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/stale/versions/2026-07-22-submitted.STALE-WRONG-FILE.pdf +0 -0
- package/fixtures/paper-stages/twice/PIPELINE-STATUS.md +14 -0
- package/fixtures/paper-stages/twice/versions/2026-08-06-submitted.pdf +0 -0
- package/fixtures/paper-stages/twice/versions/2026-10-24-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/undeclared/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/versions/2026-08-29-camera-ready.pdf +0 -0
- package/fixtures/paper-stages/wrongsize/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/wrongsize/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-typography/clean-paper/paper.tex +29 -0
- package/fixtures/paper-typography/messy-paper/paper.tex +27 -0
- package/fixtures/pdf-facts/README.md +22 -0
- package/fixtures/pdf-facts/corrupt-font.pdf +0 -0
- package/fixtures/pdf-facts/encrypted.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.tex +28 -0
- package/fixtures/pdf-facts/t3-all.pdf +0 -0
- package/fixtures/pdf-facts/t3-all.tex +8 -0
- package/fixtures/pdf-facts/t3-mixed.pdf +0 -0
- package/fixtures/pdf-facts/t3-mixed.tex +9 -0
- package/fixtures/pdf-facts/ttf.pdf +2240 -1
- package/fixtures/pdf-facts/ttf.tex +6 -0
- package/fixtures/real-markdown-paper/baseline.json +24 -0
- package/fixtures/real-markdown-paper/baseline.mjs +48 -0
- package/fixtures/render-paper/build-clean.sh +25 -0
- package/fixtures/render-paper/build-defect.sh +15 -0
- package/fixtures/review-findings-cause/clean.md +17 -0
- package/fixtures/review-findings-cause/defect.md +14 -0
- package/fixtures/review-findings-cause/old-debt.md +14 -0
- package/fixtures/review-findings-cause/quiet-in-fence.md +16 -0
- package/fixtures/tex-build/clean.tex +21 -0
- package/fixtures/tex-build/defect.tex +24 -0
- package/fixtures/tex-build/frontmatter-clean.tex +25 -0
- package/fixtures/tex-build/frontmatter-defect.tex +23 -0
- package/fixtures/toolchain-mirror/catalog.txt +5 -0
- package/fixtures/toolchain-mirror/install-tl +27 -0
- package/fixtures/toolchain-mirror/release-texlive.txt +3 -0
- package/fixtures/toolchain-mirror/release-year +1 -0
- package/fixtures/toolchain-mirror/stub-kpsewhich +8 -0
- package/fixtures/toolchain-mirror/stub-pdflatex +3 -0
- package/fixtures/toolchain-mirror/stub-tlmgr +44 -0
- package/hooks/hooks.harness.mjs +713 -0
- package/hooks/hooks.mutations.mjs +337 -0
- package/hooks/paper-edit-guard.hook.d.mts +13 -0
- package/hooks/paper-edit-guard.hook.mjs +457 -0
- package/hooks/paper-skills-nudge.hook.mjs +136 -0
- package/hooks/paper-status-gates.hook.mjs +156 -0
- package/hooks/paper-status-gates.sh +91 -0
- package/lib/agent-cli-version.harness.mjs +165 -0
- package/lib/agent-cli-version.mjs +106 -0
- package/lib/agent-cli-version.mutations.mjs +109 -0
- package/lib/markdown.mjs +386 -0
- package/lib/mutation-driver.harness.mjs +227 -0
- package/lib/mutation-driver.mjs +397 -0
- package/lib/mutation-driver.mutations.mjs +68 -0
- package/lib/paper-config.d.mts +34 -0
- package/lib/paper-config.harness.mjs +286 -0
- package/lib/paper-config.mjs +142 -0
- package/lib/paper-config.mutations.mjs +143 -0
- package/lib/skill-checks.mjs +701 -0
- package/lib/skill-corpus.mjs +403 -0
- package/lib/skill-eval-fixture.mjs +63 -0
- package/lib/skill-eval-kit.mjs +257 -0
- package/lib/skill-trigger-cases.harness.mjs +170 -0
- package/lib/skill-trigger-cases.mjs +446 -0
- package/lib/skill-trigger-cases.mutations.mjs +65 -0
- package/lib/trigger-ledger.mjs +215 -0
- package/package.json +97 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/hooks/hooks.json +30 -0
- package/scripts/check.harness.mjs +177 -0
- package/scripts/check.mjs +239 -0
- package/scripts/check.mutations.mjs +110 -0
- package/scripts/eslint-report-guard.mjs +82 -0
- package/scripts/exclusive.mjs +138 -0
- package/scripts/harness-api.frozen.json +76 -0
- package/scripts/harness-api.test.ts +175 -0
- package/scripts/layer-legacy-frozen.d.mts +28 -0
- package/scripts/layer-legacy-frozen.mjs +152 -0
- package/scripts/layer-legacy-frozen.test.ts +115 -0
- package/scripts/layer-legacy.frozen.json +50 -0
- package/scripts/mutation-batteries-frozen.harness.mjs +204 -0
- package/scripts/mutation-batteries-frozen.mjs +238 -0
- package/scripts/mutation-batteries.frozen.json +117 -0
- package/scripts/release-config.test.ts +90 -0
- package/scripts/rules-are-content-only.harness.mjs +113 -0
- package/scripts/rules-are-content-only.mjs +138 -0
- package/scripts/rules-are-content-only.mutations.mjs +81 -0
- package/scripts/rules-see-files.harness.mjs +115 -0
- package/scripts/rules-see-files.mjs +99 -0
- package/scripts/rules-see-files.mutations.mjs +131 -0
- package/scripts/run-mutations.mjs +100 -0
- package/scripts/semantic-release-plugins.d.ts +16 -0
- package/skills/README.md +15 -0
- package/skills/analyze-sibling-paper/SKILL.md +170 -0
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +186 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.eval.mjs +19 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.harness.mjs +23 -0
- package/skills/argument-arc/SKILL.md +177 -0
- package/skills/argument-arc/SKILL.md.spec.ts +192 -0
- package/skills/argument-arc/argument-arc.eval.mjs +19 -0
- package/skills/argument-arc/argument-arc.harness.mjs +23 -0
- package/skills/build-benchmark/SKILL.md +213 -0
- package/skills/build-benchmark/SKILL.md.spec.ts +220 -0
- package/skills/build-benchmark/build-benchmark.eval.mjs +19 -0
- package/skills/build-benchmark/build-benchmark.harness.mjs +23 -0
- package/skills/build-benchmark/references/adversarial-cold-repro.md +68 -0
- package/skills/camera-ready/SKILL.md +148 -0
- package/skills/camera-ready/SKILL.md.spec.ts +164 -0
- package/skills/camera-ready/camera-ready.eval.mjs +19 -0
- package/skills/camera-ready/camera-ready.harness.mjs +23 -0
- package/skills/cold-read-diff/SKILL.md +160 -0
- package/skills/cold-read-diff/SKILL.md.spec.ts +166 -0
- package/skills/cold-read-diff/cold-read-diff.eval.mjs +19 -0
- package/skills/cold-read-diff/cold-read-diff.harness.mjs +23 -0
- package/skills/draft-paper/SKILL.md +152 -0
- package/skills/draft-paper/SKILL.md.spec.ts +169 -0
- package/skills/draft-paper/draft-paper.eval.mjs +19 -0
- package/skills/draft-paper/draft-paper.harness.mjs +23 -0
- package/skills/extend-paper/SKILL.md +99 -0
- package/skills/extend-paper/SKILL.md.spec.ts +116 -0
- package/skills/extend-paper/extend-paper.eval.mjs +19 -0
- package/skills/extend-paper/extend-paper.harness.mjs +23 -0
- package/skills/find-venue/SKILL.md +128 -0
- package/skills/find-venue/SKILL.md.spec.ts +145 -0
- package/skills/find-venue/find-venue.eval.mjs +19 -0
- package/skills/find-venue/find-venue.harness.mjs +23 -0
- package/skills/grade-paper-writing/SKILL.md +436 -0
- package/skills/grade-paper-writing/SKILL.md.spec.ts +453 -0
- package/skills/grade-paper-writing/fixtures/control_gopen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/control_human_paper.txt +1 -0
- package/skills/grade-paper-writing/fixtures/rewrite.txt +1 -0
- package/skills/grade-paper-writing/fixtures/specimen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/structure-checks.md +22 -0
- package/skills/grade-paper-writing/grade-paper-writing.eval.mjs +19 -0
- package/skills/grade-paper-writing/grade-paper-writing.harness.mjs +23 -0
- package/skills/grade-paper-writing/prose-lint.mjs +713 -0
- package/skills/harden-paper/SKILL.md +318 -0
- package/skills/harden-paper/SKILL.md.spec.ts +336 -0
- package/skills/harden-paper/check-numbers.sh +33 -0
- package/skills/harden-paper/check-release-claims.sh +35 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.md +43 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +77 -0
- package/skills/harden-paper/harden-paper.eval.mjs +19 -0
- package/skills/harden-paper/harden-paper.harness.mjs +23 -0
- package/skills/map-prior-work/SKILL.md +211 -0
- package/skills/map-prior-work/SKILL.md.spec.ts +227 -0
- package/skills/map-prior-work/map-prior-work.eval.mjs +19 -0
- package/skills/map-prior-work/map-prior-work.harness.mjs +23 -0
- package/skills/osf-artifact-upload/SKILL.md +52 -0
- package/skills/osf-artifact-upload/SKILL.md.spec.ts +59 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.eval.mjs +22 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.harness.mjs +103 -0
- package/skills/paper-adversarial-review/SKILL.md +126 -0
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +142 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.eval.mjs +19 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.harness.mjs +23 -0
- package/skills/paper-pipeline/PIPELINE-MAP.md +371 -0
- package/skills/paper-pipeline/SKILL.md +499 -0
- package/skills/paper-pipeline/SKILL.md.spec.ts +517 -0
- package/skills/paper-pipeline/description-language.eval.mjs +347 -0
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +891 -0
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1254 -0
- package/skills/paper-pipeline/paper-pipeline.eval.mjs +22 -0
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +143 -0
- package/skills/paper-pipeline/pipeline-firing.baseline.json +270 -0
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +664 -0
- package/skills/paper-pipeline/pipeline-language.eval.mjs +672 -0
- package/skills/paper-pipeline/references/acceptance-gate.md +329 -0
- package/skills/paper-pipeline/references/acl-venue-rules.md +142 -0
- package/skills/paper-pipeline/references/anonymization.md +68 -0
- package/skills/paper-pipeline/references/artifact-checklist.md +93 -0
- package/skills/paper-pipeline/references/body-vs-appendix.md +97 -0
- package/skills/paper-pipeline/references/credit-criteria.md +69 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/README.md +35 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/run_retext.mjs +24 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/sentences.txt +11 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/test_sentences.py +25 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-prose-checkers.md +538 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-reproducible-tooling.md +431 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-staleness-and-orchestration.md +592 -0
- package/skills/paper-pipeline/references/pipeline-status-template.md +162 -0
- package/skills/paper-pipeline/references/review-ratchet.md +36 -0
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +585 -0
- package/skills/paper-pipeline/references/writing-craft.md +448 -0
- package/skills/paper-pipeline/repro/2026-08-07-description-language-control.log +63 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +52 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +166 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.json +338 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +118 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-raw.log +245 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary.json +776 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +53 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-raw.log +89 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.json +450 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +136 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-raw.log +242 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-setupdiff.log +59 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation.json +1032 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.json +139 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +98 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +92 -0
- package/skills/paper-pipeline/repro/README.md +129 -0
- package/skills/paper-pipeline/repro/analyze-language-eval.py +116 -0
- package/skills/paper-pipeline/scripts/README.md +344 -0
- package/skills/paper-pipeline/scripts/announce.mjs +67 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +496 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +397 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mutations.mjs +218 -0
- package/skills/paper-pipeline/scripts/check-provenance.mjs +184 -0
- package/skills/paper-pipeline/scripts/consumer.d.mts +32 -0
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +562 -0
- package/skills/paper-pipeline/scripts/consumer.mjs +535 -0
- package/skills/paper-pipeline/scripts/consumer.mutations.mjs +190 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +457 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +656 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +51 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +52 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +6 -0
- package/skills/paper-pipeline/scripts/fixtures/real-bib/refs.bib +153 -0
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +466 -0
- package/skills/paper-pipeline/scripts/generated-code.mjs +338 -0
- package/skills/paper-pipeline/scripts/generated-code.mutations.mjs +254 -0
- package/skills/paper-pipeline/scripts/ledger.mjs +623 -0
- package/skills/paper-pipeline/scripts/ledger.selftest.mjs +286 -0
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +389 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +737 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +169 -0
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +178 -0
- package/skills/paper-pipeline/scripts/population-map.mjs +181 -0
- package/skills/paper-pipeline/scripts/population-map.mutations.mjs +65 -0
- package/skills/paper-pipeline/scripts/population-map.selftest.mjs +122 -0
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +240 -0
- package/skills/paper-pipeline/scripts/provenance.mutations.mjs +59 -0
- package/skills/paper-pipeline/scripts/round-diff.harness.mjs +881 -0
- package/skills/paper-pipeline/scripts/round-diff.mjs +576 -0
- package/skills/paper-pipeline/scripts/round-diff.mutations.mjs +276 -0
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +633 -0
- package/skills/paper-pipeline/scripts/status.mjs +295 -0
- package/skills/paper-status/SKILL.md +183 -0
- package/skills/paper-status/SKILL.md.spec.ts +190 -0
- package/skills/paper-status/paper-status.eval.mjs +22 -0
- package/skills/paper-status/paper-status.harness.mjs +25 -0
- package/skills/pc-panel-review/SKILL.md +263 -0
- package/skills/pc-panel-review/SKILL.md.spec.ts +280 -0
- package/skills/pc-panel-review/pc-panel-review.eval.mjs +19 -0
- package/skills/pc-panel-review/pc-panel-review.harness.mjs +23 -0
- package/skills/plan-paper-timeline/SKILL.md +182 -0
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +200 -0
- package/skills/plan-paper-timeline/fixtures/fake-google-calendar.mjs +239 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +431 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +65 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.eval.mjs +19 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.harness.mjs +23 -0
- package/skills/render-paper/SKILL.md +159 -0
- package/skills/render-paper/SKILL.md.spec.ts +166 -0
- package/skills/render-paper/check-render.sh +419 -0
- package/skills/render-paper/checkers-requirements.txt +55 -0
- package/skills/render-paper/ensure-checkers.sh +69 -0
- package/skills/render-paper/extract-pdf-facts.harness.mjs +166 -0
- package/skills/render-paper/extract-pdf-facts.mjs +144 -0
- package/skills/render-paper/render-paper.eval.mjs +19 -0
- package/skills/render-paper/render-paper.harness.mjs +339 -0
- package/skills/research-ideate/SKILL.md +136 -0
- package/skills/research-ideate/SKILL.md.spec.ts +152 -0
- package/skills/research-ideate/research-ideate.eval.mjs +19 -0
- package/skills/research-ideate/research-ideate.harness.mjs +23 -0
- package/skills/skill-contract.mutations.mjs +179 -0
- package/skills/study-accepted-papers/SKILL.md +206 -0
- package/skills/study-accepted-papers/SKILL.md.spec.ts +223 -0
- package/skills/study-accepted-papers/study-accepted-papers.eval.mjs +19 -0
- package/skills/study-accepted-papers/study-accepted-papers.harness.mjs +23 -0
- package/skills/submit-paper/SKILL.md +182 -0
- package/skills/submit-paper/SKILL.md.spec.ts +199 -0
- package/skills/submit-paper/check-deanon.sh +149 -0
- package/skills/submit-paper/references/publishers/acm.md +92 -0
- package/skills/submit-paper/references/venues/agenticdev.jsonc +108 -0
- package/skills/submit-paper/references/venues/agenticdev.md +139 -0
- package/skills/submit-paper/references/venues/agenticdev.tex +19 -0
- package/skills/submit-paper/references/venues/aisec.jsonc +101 -0
- package/skills/submit-paper/references/venues/aisec.md +105 -0
- package/skills/submit-paper/references/venues/paper-guards.tex +41 -0
- package/skills/submit-paper/references/venues/realm.jsonc +81 -0
- package/skills/submit-paper/references/venues/realm.md +155 -0
- package/skills/submit-paper/references/venues/tex-base.jsonc +50 -0
- package/skills/submit-paper/references/venues/venue-profile.schema.json +74 -0
- package/skills/submit-paper/submit-paper.eval.mjs +19 -0
- package/skills/submit-paper/submit-paper.harness.mjs +23 -0
- package/skills/sweep-design-space/SKILL.md +269 -0
- package/skills/sweep-design-space/SKILL.md.spec.ts +285 -0
- package/skills/sweep-design-space/sweep-design-space.eval.mjs +19 -0
- package/skills/sweep-design-space/sweep-design-space.harness.mjs +23 -0
- package/skills/tighten-paper/SKILL.md +368 -0
- package/skills/tighten-paper/SKILL.md.spec.ts +384 -0
- package/skills/tighten-paper/structure.mjs +371 -0
- package/skills/tighten-paper/tighten-paper.eval.mjs +19 -0
- package/skills/tighten-paper/tighten-paper.harness.mjs +23 -0
- package/skills/verify-citations/SKILL.md +328 -0
- package/skills/verify-citations/SKILL.md.spec.ts +345 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +479 -0
- package/skills/verify-citations/scripts/bib-authors.test.mjs +175 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +1108 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +735 -0
- package/skills/verify-citations/verify-citations.eval.mjs +19 -0
- package/skills/verify-citations/verify-citations.harness.mjs +23 -0
- package/src/CLAUDE.md +51 -0
- package/src/action-ref.test.ts +26 -0
- package/src/action-ref.ts +15 -0
- package/src/adapters/banal/failure.test.ts +63 -0
- package/src/adapters/banal/failure.ts +118 -0
- package/src/adapters/banal/index.test.ts +119 -0
- package/src/adapters/banal/index.ts +100 -0
- package/src/adapters/banal/install.test.ts +20 -0
- package/src/adapters/banal/install.ts +41 -0
- package/src/adapters/banal/invocation.test.ts +74 -0
- package/src/adapters/banal/invocation.ts +95 -0
- package/src/adapters/banal/locate.test.ts +52 -0
- package/src/adapters/banal/locate.ts +84 -0
- package/src/adapters/banal/output.test.ts +140 -0
- package/src/adapters/banal/output.ts +141 -0
- package/src/adapters/banal/pin.ts +30 -0
- package/src/adapters/banal/probe.ts +35 -0
- package/src/adapters/banal/run.test.ts +191 -0
- package/src/adapters/banal/run.ts +244 -0
- package/src/adapters/banal/settings.test.ts +31 -0
- package/src/adapters/banal/settings.ts +55 -0
- package/src/adapters/banal/xml.test.ts +111 -0
- package/src/adapters/banal/xml.ts +112 -0
- package/src/adapters/curl/download.io.ts +73 -0
- package/src/adapters/curl/download.test.ts +55 -0
- package/src/adapters/curl/index.ts +5 -0
- package/src/adapters/memory/index.ts +131 -0
- package/src/adapters/node/files.io.ts +39 -0
- package/src/adapters/node/files.test.ts +28 -0
- package/src/adapters/node/host.io.ts +15 -0
- package/src/adapters/node/index.ts +36 -0
- package/src/adapters/node/process.io.ts +49 -0
- package/src/adapters/node/process.test.ts +46 -0
- package/src/adapters/node/workspace.io.ts +40 -0
- package/src/adapters/node/workspace.test.ts +58 -0
- package/src/adapters/pdfjs/fill.test.ts +111 -0
- package/src/adapters/pdfjs/fill.ts +141 -0
- package/src/build-engine.harness.mjs +314 -0
- package/src/build-engine.ts +219 -0
- package/src/build.harness.mjs +631 -0
- package/src/build.mutations.mjs +195 -0
- package/src/build.ts +793 -0
- package/src/cli.harness.mjs +2007 -0
- package/src/cli.mutations.mjs +448 -0
- package/src/cli.ts +1189 -0
- package/src/doctor.harness.mjs +396 -0
- package/src/doctor.mutations.mjs +175 -0
- package/src/doctor.ts +356 -0
- package/src/domain/geometry.ts +108 -0
- package/src/domain/host.ts +23 -0
- package/src/domain/page-layout.ts +32 -0
- package/src/domain/paths.ts +5 -0
- package/src/domain/result.test.ts +26 -0
- package/src/domain/result.ts +29 -0
- package/src/domain/sha256.test.ts +12 -0
- package/src/domain/sha256.ts +21 -0
- package/src/domain/text.ts +11 -0
- package/src/engine.harness.mjs +252 -0
- package/src/engine.ts +176 -0
- package/src/exit-code.test.ts +21 -0
- package/src/exit-code.ts +38 -0
- package/src/facts-file.test.ts +240 -0
- package/src/facts-file.ts +241 -0
- package/src/hooks-settings.harness.mjs +386 -0
- package/src/hooks-settings.mutations.mjs +116 -0
- package/src/hooks-settings.ts +434 -0
- package/src/init.ts +900 -0
- package/src/latex-log.harness.mjs +226 -0
- package/src/latex-log.ts +234 -0
- package/src/latex-loop.harness.mjs +449 -0
- package/src/latex-loop.ts +211 -0
- package/src/link-skills.harness.mjs +273 -0
- package/src/link-skills.mutations.mjs +136 -0
- package/src/link-skills.ts +258 -0
- package/src/new-paper.harness.mjs +216 -0
- package/src/new-paper.mutations.mjs +79 -0
- package/src/new-paper.ts +158 -0
- package/src/pdf-facts.harness.mjs +188 -0
- package/src/pdf-facts.ts +327 -0
- package/src/pdf-geometry.harness.mjs +254 -0
- package/src/pdf-geometry.ts +300 -0
- package/src/ports/download.ts +10 -0
- package/src/ports/files.ts +11 -0
- package/src/ports/measure-geometry.ts +8 -0
- package/src/ports/process.ts +46 -0
- package/src/ports/tool-installer.ts +33 -0
- package/src/ports/workspace.ts +20 -0
- package/src/rules-config.harness.mjs +114 -0
- package/src/rules-config.ts +178 -0
- package/src/structure.harness.mjs +179 -0
- package/src/structure.mutations.mjs +83 -0
- package/src/structure.ts +166 -0
- package/src/tex-requirements.harness.mjs +238 -0
- package/src/tex-requirements.ts +181 -0
- package/src/toolchain.harness.mjs +651 -0
- package/src/toolchain.ts +755 -0
- package/src/types.ts +106 -0
- package/templates/paper/PIPELINE-STATUS.md +72 -0
- package/templates/paper/paper.md +4 -0
- package/templates/paper/paper.tex +8 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* READ A FINISHED PDF with pdf.js — the shell around `pdf-geometry.ts` and `adapters/banal/xml.ts`. Page
|
|
3
|
+
* count, the fonts the pages draw text with, the last page's words, and every page's text boxes
|
|
4
|
+
* (the input banal measures page geometry from, written as pdftohtml XML by `adapters/banal/xml.ts`).
|
|
5
|
+
*
|
|
6
|
+
* pdf.js arrives as the npm package `unpdf` (a zero-dependency build of Mozilla's pdf.js), so
|
|
7
|
+
* reading a PDF needs nothing installed on the system. It replaced three poppler programs
|
|
8
|
+
* (`pdfinfo`, `pdffonts`, `pdftotext -bbox`), which every consumer had to install by hand; the
|
|
9
|
+
* equivalence was measured before the switch (issue #61, see `pdf-geometry.ts`).
|
|
10
|
+
*
|
|
11
|
+
* ── THE RESULT IS A UNION, AND A FAILED READ CANNOT LOOK LIKE A CLEAN ONE ─────
|
|
12
|
+
* `readPdf` never throws for a property of the file: an unreadable or encrypted PDF, and a PDF whose
|
|
13
|
+
* pages draw text while pdf.js resolves no font for it, come back as `ok: false` with a reason. The
|
|
14
|
+
* last case is the one that matters: on Node 20 pdf.js returns zero fonts and no error, and an empty
|
|
15
|
+
* font list reads as "no Type 3, nothing unembedded" — a clean verdict over nothing.
|
|
16
|
+
*
|
|
17
|
+
* ── LAZY ────────────────────────────────────────────────────────────────────────
|
|
18
|
+
* `unpdf` is imported on the first read, not when this module loads: `paperlint lint` imports the build
|
|
19
|
+
* and must not pay pdf.js's start-up for a command that never opens a PDF.
|
|
20
|
+
*/
|
|
21
|
+
// eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
|
|
22
|
+
import { readFileSync } from "node:fs";
|
|
23
|
+
import { DEFAULT_ASCENT, DEFAULT_DESCENT, fontsOf, wordsOf, } from "./pdf-geometry.js";
|
|
24
|
+
// eslint-disable-next-line boundaries/dependencies -- legacy layer, moves behind a port in #76
|
|
25
|
+
import { fillsOf, isUpright } from "./adapters/pdfjs/fill.js";
|
|
26
|
+
/**
|
|
27
|
+
* The options every read uses, exactly as measured in the spike. `fontExtraProperties` is
|
|
28
|
+
* load-bearing: without it pdf.js exports no `type`/`subtype`, and every font's program is unknown.
|
|
29
|
+
* `disableFontFace` keeps pdf.js from trying to install fonts into a page that does not exist here.
|
|
30
|
+
*/
|
|
31
|
+
export const PDFJS_OPTIONS = {
|
|
32
|
+
disableFontFace: true,
|
|
33
|
+
fontExtraProperties: true,
|
|
34
|
+
verbosity: 0,
|
|
35
|
+
useSystemFonts: false,
|
|
36
|
+
isEvalSupported: false,
|
|
37
|
+
};
|
|
38
|
+
let pdfjs = null;
|
|
39
|
+
function loadPdfJs() {
|
|
40
|
+
// eslint-disable-next-line boundaries/dependencies -- legacy layer, moves behind a port in #76
|
|
41
|
+
pdfjs ??= import("unpdf").then((m) => m.getResolvedPDFJS());
|
|
42
|
+
return pdfjs;
|
|
43
|
+
}
|
|
44
|
+
const fail = (reason, detail) => ({
|
|
45
|
+
ok: false,
|
|
46
|
+
reason,
|
|
47
|
+
detail,
|
|
48
|
+
});
|
|
49
|
+
/** A font object pdf.js left in `commonObjs`, read into a `RawFont`, or null when it is not one. */
|
|
50
|
+
export function rawFontOf(id, obj) {
|
|
51
|
+
if (typeof obj !== "object" || obj === null)
|
|
52
|
+
return null;
|
|
53
|
+
const o = obj;
|
|
54
|
+
if (!("loadedName" in o) && !("isType3Font" in o))
|
|
55
|
+
return null;
|
|
56
|
+
const str = (v) => typeof v === "string" ? v : undefined;
|
|
57
|
+
return {
|
|
58
|
+
id,
|
|
59
|
+
name: str(o["name"]) ?? "",
|
|
60
|
+
type: str(o["type"]),
|
|
61
|
+
subtype: str(o["subtype"]),
|
|
62
|
+
isType3Font: o["isType3Font"] === true,
|
|
63
|
+
missingFile: o["missingFile"] === true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const isText = (it) => "str" in it && it.str.trim() !== "";
|
|
67
|
+
/** The font pdf.js resolved for `id`, if it did. `get` throws on an unresolved id; `has` does not. */
|
|
68
|
+
function resolved(page, id) {
|
|
69
|
+
return page.commonObjs.has(id) ? page.commonObjs.get(id) : null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The fonts pdf.js has loaded for the document so far, by id. `commonObjs` is shared by every page
|
|
73
|
+
* and holds fonts alongside other objects; `rawFontOf` keeps only the fonts.
|
|
74
|
+
*/
|
|
75
|
+
function loadedFonts(page) {
|
|
76
|
+
const out = new Map();
|
|
77
|
+
for (const [id, obj] of page.commonObjs) {
|
|
78
|
+
const f = rawFontOf(id, obj);
|
|
79
|
+
if (f)
|
|
80
|
+
out.set(id, f);
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* One page: its text items, and the fonts it drew with — the fonts its text items name, plus every
|
|
86
|
+
* font first loaded while its operator list ran. The second half is not redundant: pdf.js can
|
|
87
|
+
* render a run in a font that no text item names (measured on an all-Type-3 page: four fonts
|
|
88
|
+
* loaded, two named by items; poppler lists four). A page that reuses an earlier page's fonts loads
|
|
89
|
+
* none, and its items name them.
|
|
90
|
+
*/
|
|
91
|
+
async function readPage(page, seen, lib) {
|
|
92
|
+
// The operator list is what makes pdf.js load a page's fonts into `commonObjs`.
|
|
93
|
+
const ops = await page.getOperatorList();
|
|
94
|
+
const items = (await page.getTextContent()).items.filter(isText);
|
|
95
|
+
const loaded = loadedFonts(page);
|
|
96
|
+
const named = new Set(items.map((it) => it.fontName));
|
|
97
|
+
const fonts = [...loaded.values()].filter((f) => named.has(f.id) || !seen.has(f.id));
|
|
98
|
+
return {
|
|
99
|
+
raw: { hasText: items.length > 0, fonts },
|
|
100
|
+
items,
|
|
101
|
+
layout: layoutOf(page, items, ops, lib),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A font's ascent and descent as fractions of its size, or the defaults when pdf.js has none.
|
|
106
|
+
*
|
|
107
|
+
* ⚠️ Type 3 fonts get the defaults: pdf.js exports no ascent for them. The obvious substitute,
|
|
108
|
+
* FontBBox scaled by FontMatrix (what poppler uses), was measured on `fixtures/pdf-facts/t3-all.pdf`
|
|
109
|
+
* and came out FURTHER from poppler than the defaults: 8.5 pt off on the left column against 7.2.
|
|
110
|
+
* Both are far below any balance tolerance, so the simpler rule stays.
|
|
111
|
+
*/
|
|
112
|
+
function metricsOf(font) {
|
|
113
|
+
const f = (font ?? {});
|
|
114
|
+
const ascent = f["ascent"];
|
|
115
|
+
if (typeof ascent !== "number" || !(ascent > 0))
|
|
116
|
+
return { ascent: DEFAULT_ASCENT, descent: DEFAULT_DESCENT };
|
|
117
|
+
const d = f["descent"];
|
|
118
|
+
return { ascent, descent: typeof d === "number" ? d : DEFAULT_DESCENT };
|
|
119
|
+
}
|
|
120
|
+
/** The last page's text runs in top-down coordinates, and its size. */
|
|
121
|
+
function pageText(page, items) {
|
|
122
|
+
const vp = page.getViewport({ scale: 1 });
|
|
123
|
+
const runs = items.map((it) => {
|
|
124
|
+
const [a = 0, b = 0, , d = 0, e = 0, f = 0] = it.transform;
|
|
125
|
+
const [x, baseline] = vp.convertToViewportPoint(e, f);
|
|
126
|
+
const size = Math.hypot(a, b) || it.height || Math.abs(d);
|
|
127
|
+
return {
|
|
128
|
+
text: it.str,
|
|
129
|
+
x,
|
|
130
|
+
baseline,
|
|
131
|
+
width: it.width,
|
|
132
|
+
size,
|
|
133
|
+
...metricsOf(resolved(page, it.fontName)),
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
widthPt: vp.width,
|
|
138
|
+
heightPt: vp.height,
|
|
139
|
+
words: runs.flatMap(wordsOf),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** The font name pdf.js resolved for an item, else its internal id. */
|
|
143
|
+
function fontNameOf(font, id) {
|
|
144
|
+
const name = font?.name;
|
|
145
|
+
return typeof name === "string" && name ? name : id;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* One text item as a box, in points, top-down. The size is the item's vertical scale, which is what
|
|
149
|
+
* pdftohtml reports as the font size; upright is judged in VIEWER space (the page's /Rotate applied),
|
|
150
|
+
* because that is where pdftohtml draws.
|
|
151
|
+
*/
|
|
152
|
+
function boxOf(page, vp, it, lib) {
|
|
153
|
+
const [a = 0, b = 0, c = 0, d = 0, e = 0, f = 0] = it.transform;
|
|
154
|
+
const [x, baseline] = vp.convertToViewportPoint(e, f);
|
|
155
|
+
const size = Math.hypot(c, d) || Math.hypot(a, b) || it.height;
|
|
156
|
+
const font = resolved(page, it.fontName);
|
|
157
|
+
const m = metricsOf(font);
|
|
158
|
+
return {
|
|
159
|
+
top: baseline - m.ascent * size,
|
|
160
|
+
left: x,
|
|
161
|
+
width: it.width,
|
|
162
|
+
height: (m.ascent - m.descent) * size,
|
|
163
|
+
size,
|
|
164
|
+
font: fontNameOf(font, it.fontName),
|
|
165
|
+
text: it.str,
|
|
166
|
+
upright: isUpright(lib.Util.transform(vp.transform, it.transform)),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** Every text item of a page as a box with its fill — the input of `pdf2xml`. */
|
|
170
|
+
function layoutOf(page, items, ops, lib) {
|
|
171
|
+
const vp = page.getViewport({ scale: 1 });
|
|
172
|
+
const fills = fillsOf(lib.OPS, ops, items.map((it) => it.str));
|
|
173
|
+
const boxes = items.map((it, k) => ({
|
|
174
|
+
...boxOf(page, vp, it, lib),
|
|
175
|
+
fill: fills[k] ?? { kind: "unknown" },
|
|
176
|
+
}));
|
|
177
|
+
return { widthPt: vp.width, heightPt: vp.height, boxes };
|
|
178
|
+
}
|
|
179
|
+
/** Every page's fonts, and the last page's words. */
|
|
180
|
+
async function factsOf(doc, lib) {
|
|
181
|
+
const read = [];
|
|
182
|
+
const seen = new Set();
|
|
183
|
+
for (let i = 1; i <= doc.numPages; i++) {
|
|
184
|
+
const page = await doc.getPage(i);
|
|
185
|
+
const r = await readPage(page, seen, lib);
|
|
186
|
+
for (const f of r.raw.fonts)
|
|
187
|
+
seen.add(f.id);
|
|
188
|
+
read.push({ page, ...r });
|
|
189
|
+
}
|
|
190
|
+
const [last] = read.slice(-1);
|
|
191
|
+
if (!last)
|
|
192
|
+
return fail("empty", "the PDF has no pages");
|
|
193
|
+
const fonts = fontsOf(read.map((r) => r.raw));
|
|
194
|
+
if (fonts.kind === "zero-fonts-on-text")
|
|
195
|
+
return fail("zero-fonts-on-text", `page(s) ${fonts.pages.join(", ")} draw text, but pdf.js resolved no font for it ` +
|
|
196
|
+
// eslint-disable-next-line no-restricted-globals -- legacy I/O, moves behind a port in #76
|
|
197
|
+
`(Node ${process.version}; paperlint needs Node >= 22.13, where pdf.js reports fonts)`);
|
|
198
|
+
return {
|
|
199
|
+
ok: true,
|
|
200
|
+
facts: {
|
|
201
|
+
pages: doc.numPages,
|
|
202
|
+
fonts,
|
|
203
|
+
last: pageText(last.page, last.items),
|
|
204
|
+
layout: read.map((r) => r.layout),
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/** pdf.js's own exception names for the two conditions a caller can act on. */
|
|
209
|
+
function failureOf(e) {
|
|
210
|
+
const err = e;
|
|
211
|
+
const detail = `${err.name ?? "Error"}: ${err.message ?? String(e)}`;
|
|
212
|
+
return fail(err.name === "PasswordException" ? "encrypted" : "unreadable", detail);
|
|
213
|
+
}
|
|
214
|
+
/** Read a PDF from disk. Never throws for a property of the file; see the module header. */
|
|
215
|
+
export const readPdf = async (path) => {
|
|
216
|
+
let data;
|
|
217
|
+
try {
|
|
218
|
+
data = new Uint8Array(readFileSync(path));
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
return fail("unreadable", e.message);
|
|
222
|
+
}
|
|
223
|
+
const lib = await loadPdfJs();
|
|
224
|
+
const task = lib.getDocument({ data, ...PDFJS_OPTIONS });
|
|
225
|
+
try {
|
|
226
|
+
return await factsOf(await task.promise, lib);
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
return failureOf(e);
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
await task.destroy();
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
/** The one wording of a failed read, as a build step or a CLI prints it. */
|
|
236
|
+
export function describeFailure(r, pdf) {
|
|
237
|
+
return `could not read ${pdf} with pdf.js (${r.reason}): ${r.detail}`;
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=pdf-facts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-facts.js","sourceRoot":"","sources":["../src/pdf-facts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHAT A PDF'S TEXT AND FONTS SAY, once pdf.js has read them — PURE. No disk, no pdf.js.
|
|
3
|
+
*
|
|
4
|
+
* `pdf-facts.ts` is the shell: it opens the PDF with pdf.js (the `unpdf` package) and hands this
|
|
5
|
+
* module plain data — text runs with their metrics, and the fonts each page resolved. Everything
|
|
6
|
+
* that DECIDES something is here, so every branch is a row in a table test fed with hand-written
|
|
7
|
+
* words, including pages the fixture corpus does not contain.
|
|
8
|
+
*
|
|
9
|
+
* ── WHY pdf.js AND NOT POPPLER (issue #61) ───────────────────────────────────
|
|
10
|
+
* The facts used to come from three poppler programs (`pdfinfo`, `pdffonts`, `pdftotext -bbox`), a
|
|
11
|
+
* system install paperlint could not provide and every consumer had to add by hand. A measurement on 25
|
|
12
|
+
* PDFs (2026-09-24) found pdf.js equal on page count and on the Type 3 count of every PDF, and the
|
|
13
|
+
* last page's column heights within 0.2 pt except on an all-Type-3 page (7.2 pt; pdf.js has no
|
|
14
|
+
* ascent for Type 3). No verdict flipped at a 120 pt tolerance.
|
|
15
|
+
*
|
|
16
|
+
* ── THE LAST PAGE IS A MEASUREMENT, NOT A VERDICT ────────────────────────────
|
|
17
|
+
* `classifyLastPage` returns the heights of the two columns, or says why there is nothing to
|
|
18
|
+
* measure. It carries no threshold: whether a venue requires balance, and how much difference it
|
|
19
|
+
* allows, belongs to the lint rule `pdf/last-page-balance`, which a consumer turns on per venue.
|
|
20
|
+
*/
|
|
21
|
+
/** One word on a page, in points, top-down: `y0` is the top edge, `y1` the bottom. */
|
|
22
|
+
export interface Word {
|
|
23
|
+
readonly x0: number;
|
|
24
|
+
readonly y0: number;
|
|
25
|
+
readonly x1: number;
|
|
26
|
+
readonly y1: number;
|
|
27
|
+
readonly text: string;
|
|
28
|
+
}
|
|
29
|
+
/** A page's size and its words. */
|
|
30
|
+
export interface PageText {
|
|
31
|
+
readonly widthPt: number;
|
|
32
|
+
readonly heightPt: number;
|
|
33
|
+
readonly words: readonly Word[];
|
|
34
|
+
}
|
|
35
|
+
/** The heights of the last page's two columns, in points: [left, right]. */
|
|
36
|
+
export type Columns = readonly [left: number, right: number];
|
|
37
|
+
/**
|
|
38
|
+
* What the last page is, as far as balance goes.
|
|
39
|
+
*
|
|
40
|
+
* - `measured` — the heights of both columns.
|
|
41
|
+
* - `stub` — fewer than `STUB_WORDS` words: a tail of a few lines is not a layout.
|
|
42
|
+
* - `review` — numbered lines in the outer margins. The numbers run down the whole page, so every
|
|
43
|
+
* column would measure full height; a review build is not judged at all.
|
|
44
|
+
*/
|
|
45
|
+
export type LastPage = {
|
|
46
|
+
readonly kind: "measured";
|
|
47
|
+
readonly columns: Columns;
|
|
48
|
+
} | {
|
|
49
|
+
readonly kind: "stub";
|
|
50
|
+
readonly words: number;
|
|
51
|
+
} | {
|
|
52
|
+
readonly kind: "review";
|
|
53
|
+
readonly lineNumbers: number;
|
|
54
|
+
};
|
|
55
|
+
/** Below this many words the last page is a stub. */
|
|
56
|
+
export declare const STUB_WORDS = 60;
|
|
57
|
+
/** This many short integers in the outer margins make a review build. */
|
|
58
|
+
export declare const REVIEW_LINE_NUMBERS = 5;
|
|
59
|
+
/** The outer margins, as a fraction of the page width, where line numbers sit. */
|
|
60
|
+
export declare const MARGIN_FRACTION = 0.08;
|
|
61
|
+
/**
|
|
62
|
+
* Classify the last page and, when it has a layout, measure its two columns.
|
|
63
|
+
*
|
|
64
|
+
* 🔴 THE PAGE IS SPLIT AT ITS OWN MIDDLE, not between the outermost words: on a page where only the
|
|
65
|
+
* left column is filled, the words' midpoint falls inside that column and cuts it in two ("77 / 731"
|
|
66
|
+
* for six lines at the top left, measured 2026-08-29). A word belongs to the side its left edge is on.
|
|
67
|
+
*
|
|
68
|
+
* Throws on a page with no width — the caller built `PageText` from the PDF's own viewport, so a
|
|
69
|
+
* non-positive width is a defect in the caller, not a property of a paper.
|
|
70
|
+
*/
|
|
71
|
+
export declare function classifyLastPage(p: PageText): LastPage;
|
|
72
|
+
/**
|
|
73
|
+
* One text run as pdf.js reports it, already in top-down page coordinates: where its baseline
|
|
74
|
+
* starts, how wide it is, its font size, and the font's ascent and descent as fractions of the size
|
|
75
|
+
* (descent negative, as fonts store it).
|
|
76
|
+
*/
|
|
77
|
+
export interface TextRun {
|
|
78
|
+
readonly text: string;
|
|
79
|
+
readonly x: number;
|
|
80
|
+
readonly baseline: number;
|
|
81
|
+
readonly width: number;
|
|
82
|
+
readonly size: number;
|
|
83
|
+
readonly ascent: number;
|
|
84
|
+
readonly descent: number;
|
|
85
|
+
}
|
|
86
|
+
/** A font's vertical metrics as fractions of its size, when pdf.js supplies none. */
|
|
87
|
+
export declare const DEFAULT_ASCENT = 0.8;
|
|
88
|
+
export declare const DEFAULT_DESCENT = -0.2;
|
|
89
|
+
/**
|
|
90
|
+
* Split a run into words. pdf.js gives a run's total width, not each glyph's, so a word's horizontal
|
|
91
|
+
* extent is estimated in proportion to its characters. Only `x0` decides the column a word falls
|
|
92
|
+
* in, and a run never crosses the gutter, so the estimate cannot move a word to the other side.
|
|
93
|
+
*/
|
|
94
|
+
export declare function wordsOf(run: TextRun): Word[];
|
|
95
|
+
/** The font programs a PDF can carry, in pdf.js's vocabulary. */
|
|
96
|
+
export type FontProgram = "Type1" | "Type1C" | "MMType1" | "TrueType" | "OpenType" | "CIDType0" | "CIDType0C" | "CIDTrueType" | "Type3";
|
|
97
|
+
/** A program pdf.js named but this module does not know — kept verbatim, never guessed. */
|
|
98
|
+
export type UnknownProgram = `unknown:${string}`;
|
|
99
|
+
/**
|
|
100
|
+
* One font the PDF draws text with.
|
|
101
|
+
*
|
|
102
|
+
* - `embedded` — a usable font program is in the file.
|
|
103
|
+
* - `type3` — a Type 3 font: glyphs drawn as PDF procedures, usually bitmaps. pdf.js gives
|
|
104
|
+
* every Type 3 font the same name, `Type3`, so the name carries nothing.
|
|
105
|
+
* - `not-embedded` — the PDF names the font but carries no usable program for it: either none at
|
|
106
|
+
* all, or one pdf.js could not parse. pdf.js reports both as one flag
|
|
107
|
+
* (`missingFile`), so the two are not told apart here.
|
|
108
|
+
*/
|
|
109
|
+
export type FontFact = {
|
|
110
|
+
readonly kind: "embedded";
|
|
111
|
+
readonly name: string;
|
|
112
|
+
readonly program: Exclude<FontProgram, "Type3"> | UnknownProgram;
|
|
113
|
+
} | {
|
|
114
|
+
readonly kind: "type3";
|
|
115
|
+
readonly name: "Type3";
|
|
116
|
+
} | {
|
|
117
|
+
readonly kind: "not-embedded";
|
|
118
|
+
readonly name: string;
|
|
119
|
+
readonly program: Exclude<FontProgram, "Type3"> | UnknownProgram;
|
|
120
|
+
};
|
|
121
|
+
/** A font as pdf.js exports it with `fontExtraProperties`, reduced to what is read here. */
|
|
122
|
+
export interface RawFont {
|
|
123
|
+
/** pdf.js's id for the font within the document (`loadedName`). */
|
|
124
|
+
readonly id: string;
|
|
125
|
+
readonly name: string;
|
|
126
|
+
readonly type: string | undefined;
|
|
127
|
+
readonly subtype: string | undefined;
|
|
128
|
+
readonly isType3Font: boolean;
|
|
129
|
+
readonly missingFile: boolean;
|
|
130
|
+
}
|
|
131
|
+
/** One page: whether it draws any text, and the fonts its text resolved to. */
|
|
132
|
+
export interface RawPage {
|
|
133
|
+
readonly hasText: boolean;
|
|
134
|
+
readonly fonts: readonly RawFont[];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The fonts of a document. `drawn` is non-empty BY TYPE: a document whose pages draw text has at
|
|
138
|
+
* least one font, and "no fonts" is spelled only as `no-text`.
|
|
139
|
+
*/
|
|
140
|
+
export type Fonts = {
|
|
141
|
+
readonly kind: "drawn";
|
|
142
|
+
readonly list: readonly [FontFact, ...FontFact[]];
|
|
143
|
+
} | {
|
|
144
|
+
readonly kind: "no-text";
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* 🔴 TEXT ON A PAGE WITH NO FONT BEHIND IT IS A FAILED READ, NOT AN EMPTY LIST. Measured on Node
|
|
148
|
+
* 20.20.2: pdf.js opens the same PDFs and returns ZERO fonts with no error. An empty list there
|
|
149
|
+
* would read downstream as "no Type 3, nothing unembedded" — a clean verdict over nothing.
|
|
150
|
+
*/
|
|
151
|
+
export interface ZeroFontsOnText {
|
|
152
|
+
readonly kind: "zero-fonts-on-text";
|
|
153
|
+
/** 1-based numbers of the pages that draw text with no font resolved. */
|
|
154
|
+
readonly pages: readonly number[];
|
|
155
|
+
}
|
|
156
|
+
export declare function programOf(type: string | undefined, subtype: string | undefined): Exclude<FontProgram, "Type3"> | UnknownProgram;
|
|
157
|
+
export declare function fontFact(f: RawFont): FontFact;
|
|
158
|
+
/**
|
|
159
|
+
* The document's fonts, each once, in the order pages first drew them — or the pages that drew
|
|
160
|
+
* text with no font, which the caller turns into a failed read.
|
|
161
|
+
*/
|
|
162
|
+
export declare function fontsOf(pages: readonly RawPage[]): Fonts | ZeroFontsOnText;
|
|
163
|
+
/**
|
|
164
|
+
* poppler's spelling of the font's type column (`pdffonts`), which the facts JSON has always used
|
|
165
|
+
* and consumers match on (`Type 3`).
|
|
166
|
+
*/
|
|
167
|
+
export declare const POPPLER_TYPE: Readonly<Record<FontProgram, string>>;
|
|
168
|
+
/** The type column for any fact, the unknown ones verbatim. */
|
|
169
|
+
export declare function popplerType(f: FontFact): string;
|
|
170
|
+
//# sourceMappingURL=pdf-geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-geometry.d.ts","sourceRoot":"","sources":["../src/pdf-geometry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHAT A PDF'S TEXT AND FONTS SAY, once pdf.js has read them — PURE. No disk, no pdf.js.
|
|
3
|
+
*
|
|
4
|
+
* `pdf-facts.ts` is the shell: it opens the PDF with pdf.js (the `unpdf` package) and hands this
|
|
5
|
+
* module plain data — text runs with their metrics, and the fonts each page resolved. Everything
|
|
6
|
+
* that DECIDES something is here, so every branch is a row in a table test fed with hand-written
|
|
7
|
+
* words, including pages the fixture corpus does not contain.
|
|
8
|
+
*
|
|
9
|
+
* ── WHY pdf.js AND NOT POPPLER (issue #61) ───────────────────────────────────
|
|
10
|
+
* The facts used to come from three poppler programs (`pdfinfo`, `pdffonts`, `pdftotext -bbox`), a
|
|
11
|
+
* system install paperlint could not provide and every consumer had to add by hand. A measurement on 25
|
|
12
|
+
* PDFs (2026-09-24) found pdf.js equal on page count and on the Type 3 count of every PDF, and the
|
|
13
|
+
* last page's column heights within 0.2 pt except on an all-Type-3 page (7.2 pt; pdf.js has no
|
|
14
|
+
* ascent for Type 3). No verdict flipped at a 120 pt tolerance.
|
|
15
|
+
*
|
|
16
|
+
* ── THE LAST PAGE IS A MEASUREMENT, NOT A VERDICT ────────────────────────────
|
|
17
|
+
* `classifyLastPage` returns the heights of the two columns, or says why there is nothing to
|
|
18
|
+
* measure. It carries no threshold: whether a venue requires balance, and how much difference it
|
|
19
|
+
* allows, belongs to the lint rule `pdf/last-page-balance`, which a consumer turns on per venue.
|
|
20
|
+
*/
|
|
21
|
+
/** Below this many words the last page is a stub. */
|
|
22
|
+
export const STUB_WORDS = 60;
|
|
23
|
+
/** This many short integers in the outer margins make a review build. */
|
|
24
|
+
export const REVIEW_LINE_NUMBERS = 5;
|
|
25
|
+
/** The outer margins, as a fraction of the page width, where line numbers sit. */
|
|
26
|
+
export const MARGIN_FRACTION = 0.08;
|
|
27
|
+
const round1 = (n) => Number(n.toFixed(1));
|
|
28
|
+
/** Top of the highest word to bottom of the lowest one; 0 for no words. */
|
|
29
|
+
function heightOf(words) {
|
|
30
|
+
if (words.length === 0)
|
|
31
|
+
return 0;
|
|
32
|
+
const top = Math.min(...words.map((w) => w.y0));
|
|
33
|
+
const bottom = Math.max(...words.map((w) => w.y1));
|
|
34
|
+
return round1(bottom - top);
|
|
35
|
+
}
|
|
36
|
+
/** A short integer standing in the left or right margin: a line number of a review build. */
|
|
37
|
+
function isLineNumber(w, widthPt) {
|
|
38
|
+
const margin = widthPt * MARGIN_FRACTION;
|
|
39
|
+
return (/^\d{1,4}$/.test(w.text.trim()) &&
|
|
40
|
+
(w.x1 < margin || w.x0 > widthPt - margin));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Classify the last page and, when it has a layout, measure its two columns.
|
|
44
|
+
*
|
|
45
|
+
* 🔴 THE PAGE IS SPLIT AT ITS OWN MIDDLE, not between the outermost words: on a page where only the
|
|
46
|
+
* left column is filled, the words' midpoint falls inside that column and cuts it in two ("77 / 731"
|
|
47
|
+
* for six lines at the top left, measured 2026-08-29). A word belongs to the side its left edge is on.
|
|
48
|
+
*
|
|
49
|
+
* Throws on a page with no width — the caller built `PageText` from the PDF's own viewport, so a
|
|
50
|
+
* non-positive width is a defect in the caller, not a property of a paper.
|
|
51
|
+
*/
|
|
52
|
+
export function classifyLastPage(p) {
|
|
53
|
+
if (!(p.widthPt > 0))
|
|
54
|
+
throw new RangeError(`page width must be positive, got ${p.widthPt}`);
|
|
55
|
+
if (p.words.length < STUB_WORDS)
|
|
56
|
+
return { kind: "stub", words: p.words.length };
|
|
57
|
+
const lineNumbers = p.words.filter((w) => isLineNumber(w, p.widthPt)).length;
|
|
58
|
+
if (lineNumbers >= REVIEW_LINE_NUMBERS)
|
|
59
|
+
return { kind: "review", lineNumbers };
|
|
60
|
+
const mid = p.widthPt / 2;
|
|
61
|
+
return {
|
|
62
|
+
kind: "measured",
|
|
63
|
+
columns: [
|
|
64
|
+
heightOf(p.words.filter((w) => w.x0 < mid)),
|
|
65
|
+
heightOf(p.words.filter((w) => w.x0 >= mid)),
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** A font's vertical metrics as fractions of its size, when pdf.js supplies none. */
|
|
70
|
+
export const DEFAULT_ASCENT = 0.8;
|
|
71
|
+
export const DEFAULT_DESCENT = -0.2;
|
|
72
|
+
/**
|
|
73
|
+
* Split a run into words. pdf.js gives a run's total width, not each glyph's, so a word's horizontal
|
|
74
|
+
* extent is estimated in proportion to its characters. Only `x0` decides the column a word falls
|
|
75
|
+
* in, and a run never crosses the gutter, so the estimate cannot move a word to the other side.
|
|
76
|
+
*/
|
|
77
|
+
export function wordsOf(run) {
|
|
78
|
+
const perChar = run.width / Math.max(1, run.text.length);
|
|
79
|
+
const y0 = run.baseline - run.ascent * run.size;
|
|
80
|
+
const y1 = run.baseline - run.descent * run.size;
|
|
81
|
+
return [...run.text.matchAll(/\S+/g)].map((m) => {
|
|
82
|
+
const x0 = run.x + (m.index ?? 0) * perChar;
|
|
83
|
+
return { x0, y0, x1: x0 + m[0].length * perChar, y1, text: m[0] };
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* pdf.js's `type`/`subtype` pair → the program, keyed `type/subtype`. Composite fonts carry their
|
|
88
|
+
* descendant's type. A pair not listed is kept verbatim as `unknown:…`, never guessed.
|
|
89
|
+
*/
|
|
90
|
+
const PROGRAMS = {
|
|
91
|
+
"Type1/undefined": "Type1",
|
|
92
|
+
"Type1/Type1C": "Type1C",
|
|
93
|
+
"MMType1/undefined": "MMType1",
|
|
94
|
+
"TrueType/undefined": "TrueType",
|
|
95
|
+
"CIDFontType0/undefined": "CIDType0",
|
|
96
|
+
"CIDFontType0/CIDFontType0C": "CIDType0C",
|
|
97
|
+
"CIDFontType2/undefined": "CIDTrueType",
|
|
98
|
+
};
|
|
99
|
+
export function programOf(type, subtype) {
|
|
100
|
+
// pdf.js names an OpenType-wrapped program by the wrapper, on either field, whatever it wraps.
|
|
101
|
+
if (subtype === "OpenType" || type === "OpenType")
|
|
102
|
+
return "OpenType";
|
|
103
|
+
const key = `${String(type)}/${String(subtype)}`;
|
|
104
|
+
return PROGRAMS[key] ?? `unknown:${key}`;
|
|
105
|
+
}
|
|
106
|
+
/** Subset fonts carry a six-letter tag, `ABCDEF+Name`; the name is what follows it. */
|
|
107
|
+
const withoutSubsetTag = (name) => name.replace(/^[A-Z]{6}\+/, "");
|
|
108
|
+
export function fontFact(f) {
|
|
109
|
+
if (f.isType3Font)
|
|
110
|
+
return { kind: "type3", name: "Type3" };
|
|
111
|
+
const name = withoutSubsetTag(f.name);
|
|
112
|
+
const program = programOf(f.type, f.subtype);
|
|
113
|
+
return f.missingFile
|
|
114
|
+
? { kind: "not-embedded", name, program }
|
|
115
|
+
: { kind: "embedded", name, program };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The document's fonts, each once, in the order pages first drew them — or the pages that drew
|
|
119
|
+
* text with no font, which the caller turns into a failed read.
|
|
120
|
+
*/
|
|
121
|
+
export function fontsOf(pages) {
|
|
122
|
+
const bad = pages.flatMap((p, i) => p.hasText && p.fonts.length === 0 ? [i + 1] : []);
|
|
123
|
+
if (bad.length > 0)
|
|
124
|
+
return { kind: "zero-fonts-on-text", pages: bad };
|
|
125
|
+
const seen = new Map();
|
|
126
|
+
for (const p of pages)
|
|
127
|
+
for (const f of p.fonts)
|
|
128
|
+
if (!seen.has(f.id))
|
|
129
|
+
seen.set(f.id, fontFact(f));
|
|
130
|
+
const [first, ...rest] = seen.values();
|
|
131
|
+
return first
|
|
132
|
+
? { kind: "drawn", list: [first, ...rest] }
|
|
133
|
+
: { kind: "no-text" };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* poppler's spelling of the font's type column (`pdffonts`), which the facts JSON has always used
|
|
137
|
+
* and consumers match on (`Type 3`).
|
|
138
|
+
*/
|
|
139
|
+
export const POPPLER_TYPE = {
|
|
140
|
+
Type1: "Type 1",
|
|
141
|
+
Type1C: "Type 1C",
|
|
142
|
+
MMType1: "Type 1",
|
|
143
|
+
TrueType: "TrueType",
|
|
144
|
+
OpenType: "OpenType",
|
|
145
|
+
CIDType0: "CID Type 0",
|
|
146
|
+
CIDType0C: "CID Type 0C",
|
|
147
|
+
CIDTrueType: "CID TrueType",
|
|
148
|
+
Type3: "Type 3",
|
|
149
|
+
};
|
|
150
|
+
/** The type column for any fact, the unknown ones verbatim. */
|
|
151
|
+
export function popplerType(f) {
|
|
152
|
+
if (f.kind === "type3")
|
|
153
|
+
return POPPLER_TYPE.Type3;
|
|
154
|
+
return f.program.startsWith("unknown:")
|
|
155
|
+
? f.program
|
|
156
|
+
: POPPLER_TYPE[f.program];
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=pdf-geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-geometry.js","sourceRoot":"","sources":["../src/pdf-geometry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Fetching a URL — the generic port for the network. */
|
|
2
|
+
import type { Result } from "../domain/result.ts";
|
|
3
|
+
/** Fetch a URL's bytes. */
|
|
4
|
+
export interface Download {
|
|
5
|
+
fetch(url: string, timeoutMs: number): Result<Uint8Array, {
|
|
6
|
+
readonly detail: string;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=download.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/ports/download.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,2BAA2B;AAC3B,MAAM,WAAW,QAAQ;IACvB,KAAK,CACH,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/ports/download.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Files by path — the generic port for the disk. */
|
|
2
|
+
import type { AbsolutePath } from "../domain/paths.ts";
|
|
3
|
+
/** Files by path. A read that fails for any reason but absence throws: that is an adapter bug. */
|
|
4
|
+
export interface Files {
|
|
5
|
+
isFile(p: AbsolutePath): boolean;
|
|
6
|
+
/** The file's bytes, or null when there is no such file. */
|
|
7
|
+
readBytes(p: AbsolutePath): Uint8Array | null;
|
|
8
|
+
/** Write through `<p>.part` and a rename, creating the directory: a reader never sees half a file. */
|
|
9
|
+
writeAtomic(p: AbsolutePath, bytes: Uint8Array): void;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/ports/files.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,kGAAkG;AAClG,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC;IACjC,4DAA4D;IAC5D,SAAS,CAAC,CAAC,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI,CAAC;IAC9C,sGAAsG;IACtG,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACvD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/ports/files.ts"],"names":[],"mappings":""}
|