paperlint 3.1.3 → 4.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/workflows/ci.yml +12 -7
- package/CLAUDE.md +5 -5
- package/CONTRIBUTING.md +34 -5
- package/dist/adapters/banal/failure.d.ts +1 -7
- package/dist/adapters/banal/failure.d.ts.map +1 -1
- package/dist/adapters/banal/failure.js +5 -7
- package/dist/adapters/banal/failure.js.map +1 -1
- package/dist/adapters/banal/index.d.ts.map +1 -1
- package/dist/adapters/banal/index.js +4 -3
- package/dist/adapters/banal/index.js.map +1 -1
- package/dist/adapters/banal/install.d.ts.map +1 -1
- package/dist/adapters/banal/install.js +4 -3
- package/dist/adapters/banal/install.js.map +1 -1
- package/dist/adapters/banal/invocation.d.ts +10 -5
- package/dist/adapters/banal/invocation.d.ts.map +1 -1
- package/dist/adapters/banal/invocation.js +13 -1
- package/dist/adapters/banal/invocation.js.map +1 -1
- package/dist/adapters/banal/locate.d.ts +12 -9
- package/dist/adapters/banal/locate.d.ts.map +1 -1
- package/dist/adapters/banal/locate.js +19 -12
- package/dist/adapters/banal/locate.js.map +1 -1
- package/dist/adapters/banal/run.d.ts +10 -1
- package/dist/adapters/banal/run.d.ts.map +1 -1
- package/dist/adapters/banal/run.js +8 -4
- package/dist/adapters/banal/run.js.map +1 -1
- package/dist/adapters/banal/settings.d.ts +1 -1
- package/dist/adapters/banal/settings.d.ts.map +1 -1
- package/dist/adapters/banal/settings.js +2 -1
- package/dist/adapters/banal/settings.js.map +1 -1
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +8 -2
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/node/files.io.d.ts.map +1 -1
- package/dist/adapters/node/files.io.js +2 -1
- package/dist/adapters/node/files.io.js.map +1 -1
- package/dist/adapters/node/process.io.d.ts +5 -7
- package/dist/adapters/node/process.io.d.ts.map +1 -1
- package/dist/adapters/node/process.io.js +4 -3
- package/dist/adapters/node/process.io.js.map +1 -1
- package/dist/adapters/node/workspace.io.d.ts.map +1 -1
- package/dist/adapters/node/workspace.io.js +4 -2
- package/dist/adapters/node/workspace.io.js.map +1 -1
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
- package/dist/adapters/pdfjs/fill.js +13 -7
- package/dist/adapters/pdfjs/fill.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +8 -7
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/references/pool.d.ts.map +1 -1
- package/dist/adapters/references/pool.js +4 -5
- package/dist/adapters/references/pool.js.map +1 -1
- package/dist/adapters/references/reach.io.d.ts.map +1 -1
- package/dist/adapters/references/reach.io.js +2 -1
- package/dist/adapters/references/reach.io.js.map +1 -1
- package/dist/build-engine.d.ts +1 -1
- package/dist/build-engine.d.ts.map +1 -1
- package/dist/build-engine.js +10 -3
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts +2 -3
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +19 -17
- package/dist/build.js.map +1 -1
- package/dist/caller-path.d.ts +3 -0
- package/dist/caller-path.d.ts.map +1 -0
- package/dist/caller-path.js +13 -0
- package/dist/caller-path.js.map +1 -0
- package/dist/cli.d.ts +10 -17
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -46
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts +12 -3
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +5 -3
- package/dist/doctor.js.map +1 -1
- package/dist/domain/lookup-cache.d.ts.map +1 -1
- package/dist/domain/lookup-cache.js +10 -8
- package/dist/domain/lookup-cache.js.map +1 -1
- package/dist/domain/paths.d.ts +8 -2
- package/dist/domain/paths.d.ts.map +1 -1
- package/dist/domain/paths.js +12 -1
- package/dist/domain/paths.js.map +1 -1
- package/dist/domain/ready.d.ts +24 -0
- package/dist/domain/ready.d.ts.map +1 -0
- package/dist/domain/ready.js +11 -0
- package/dist/domain/ready.js.map +1 -0
- package/dist/domain/record.d.ts +8 -0
- package/dist/domain/record.d.ts.map +1 -0
- package/dist/domain/record.js +8 -0
- package/dist/domain/record.js.map +1 -0
- package/dist/domain/sha256.d.ts.map +1 -1
- package/dist/domain/sha256.js +4 -2
- package/dist/domain/sha256.js.map +1 -1
- package/dist/domain/text.d.ts +12 -0
- package/dist/domain/text.d.ts.map +1 -1
- package/dist/domain/text.js +17 -0
- package/dist/domain/text.js.map +1 -1
- package/dist/engine.d.ts +23 -22
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -3
- package/dist/engine.js.map +1 -1
- package/dist/eslint-rules/bib-reachable-entry.d.ts +22 -0
- package/dist/eslint-rules/bib-reachable-entry.d.ts.map +1 -0
- package/dist/eslint-rules/bib-reachable-entry.js +75 -0
- package/dist/eslint-rules/bib-reachable-entry.js.map +1 -0
- package/dist/eslint-rules/install-path-literals.d.ts +80 -0
- package/dist/eslint-rules/install-path-literals.d.ts.map +1 -0
- package/dist/eslint-rules/install-path-literals.js +105 -0
- package/dist/eslint-rules/install-path-literals.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts +136 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -0
- package/dist/eslint-rules/latex-language.js +639 -0
- package/dist/eslint-rules/latex-language.js.map +1 -0
- package/dist/eslint-rules/paper-research-question.d.ts +31 -0
- package/dist/eslint-rules/paper-research-question.d.ts.map +1 -0
- package/dist/eslint-rules/paper-research-question.js +188 -0
- package/dist/eslint-rules/paper-research-question.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +83 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -0
- package/dist/eslint-rules/paper-stages.js +358 -0
- package/dist/eslint-rules/paper-stages.js.map +1 -0
- package/dist/eslint-rules/paper-typography.d.ts +74 -0
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -0
- package/dist/eslint-rules/paper-typography.js +404 -0
- package/dist/eslint-rules/paper-typography.js.map +1 -0
- package/dist/eslint-rules/papers.d.ts +42 -0
- package/dist/eslint-rules/papers.d.ts.map +1 -0
- package/dist/eslint-rules/papers.js +145 -0
- package/dist/eslint-rules/papers.js.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts +72 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.js +237 -0
- package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.d.ts +57 -0
- package/dist/eslint-rules/review-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/review-frontmatter.js +126 -0
- package/dist/eslint-rules/review-frontmatter.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.schema.json +36 -0
- package/dist/eslint-rules/rule-context.d.ts +55 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -0
- package/dist/eslint-rules/rule-context.js +9 -0
- package/dist/eslint-rules/rule-context.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts +25 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.js +16 -0
- package/dist/eslint-rules/sibling-frontmatter.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.schema.json +14 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts +71 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts.map +1 -0
- package/dist/eslint-rules/temp-root-realpath.js +139 -0
- package/dist/eslint-rules/temp-root-realpath.js.map +1 -0
- package/dist/eslint-rules/tex-build.d.ts +35 -0
- package/dist/eslint-rules/tex-build.d.ts.map +1 -0
- package/dist/eslint-rules/tex-build.js +313 -0
- package/dist/eslint-rules/tex-build.js.map +1 -0
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +31 -27
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts +1 -1
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +31 -11
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +8 -9
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +33 -15
- package/dist/init.js.map +1 -1
- package/dist/latex-loop.d.ts +2 -2
- package/dist/latex-loop.d.ts.map +1 -1
- package/dist/latex-loop.js +2 -2
- package/dist/latex-loop.js.map +1 -1
- package/dist/lib/agent-cli-version.d.ts +65 -0
- package/dist/lib/agent-cli-version.d.ts.map +1 -0
- package/dist/lib/agent-cli-version.js +77 -0
- package/dist/lib/agent-cli-version.js.map +1 -0
- package/dist/lib/check.d.ts +19 -0
- package/dist/lib/check.d.ts.map +1 -0
- package/dist/lib/check.js +52 -0
- package/dist/lib/check.js.map +1 -0
- package/dist/lib/markdown.d.ts +180 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +302 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/paper-config.d.ts +84 -0
- package/dist/lib/paper-config.d.ts.map +1 -0
- package/dist/lib/paper-config.js +138 -0
- package/dist/lib/paper-config.js.map +1 -0
- package/dist/lib/skill-eval-fixture.d.ts +29 -0
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -0
- package/dist/lib/skill-eval-fixture.js +62 -0
- package/dist/lib/skill-eval-fixture.js.map +1 -0
- package/dist/link-skills.d.ts.map +1 -1
- package/dist/link-skills.js +5 -4
- package/dist/link-skills.js.map +1 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +7 -3
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-files.d.ts +4 -4
- package/dist/paper-files.d.ts.map +1 -1
- package/dist/paper-files.js +6 -6
- package/dist/paper-files.js.map +1 -1
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +8 -7
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-facts.d.ts +51 -1
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +25 -16
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.d.ts.map +1 -1
- package/dist/pdf-geometry.js +3 -4
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/ports/tool-installer.d.ts +2 -14
- package/dist/ports/tool-installer.d.ts.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +19 -6
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +15 -10
- package/dist/references.js.map +1 -1
- package/dist/rules-config.d.ts +26 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +15 -12
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.js +1 -1
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.d.ts +4 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +22 -5
- package/dist/tex-requirements.js.map +1 -1
- package/dist/toolchain.d.ts.map +1 -1
- package/dist/toolchain.js +21 -16
- package/dist/toolchain.js.map +1 -1
- package/dist/types.d.ts +5 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +21 -8
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +2 -2
- package/docs/e2e.md +7 -7
- package/docs/install.md +1 -1
- package/docs/prior-art/README.md +1 -1
- package/docs/prior-art/package-location.md +5 -5
- package/docs/prior-art/repro/README.md +1 -1
- package/docs/prior-art/test-tooling.md +2 -2
- package/docs/testing.md +11 -8
- package/docs/toolchain.md +1 -1
- package/eslint-rules/{bib-reachable-entry.mjs → bib-reachable-entry.ts} +5 -4
- package/eslint-rules/install-path-literals.harness.mjs +1 -1
- package/eslint-rules/{install-path-literals.mjs → install-path-literals.ts} +18 -10
- package/eslint-rules/latex-language.harness.mjs +2 -2
- package/eslint-rules/latex-language.test.mjs +7 -1
- package/eslint-rules/{latex-language.mjs → latex-language.ts} +297 -109
- package/eslint-rules/paper-research-question.harness.mjs +3 -3
- package/eslint-rules/{paper-research-question.mjs → paper-research-question.ts} +35 -16
- package/eslint-rules/paper-stages.harness.mjs +2 -2
- package/eslint-rules/paper-stages.test.mjs +8 -2
- package/eslint-rules/{paper-stages.mjs → paper-stages.ts} +76 -31
- package/eslint-rules/paper-typography.test.mjs +3 -3
- package/eslint-rules/{paper-typography.mjs → paper-typography.ts} +134 -56
- package/eslint-rules/papers.harness.mjs +2 -2
- package/eslint-rules/{papers.mjs → papers.ts} +7 -4
- package/eslint-rules/pdf-last-page-balance.harness.mjs +3 -3
- package/eslint-rules/{pdf-last-page-balance.mjs → pdf-last-page-balance.ts} +89 -32
- package/eslint-rules/review-frontmatter.test.mjs +2 -2
- package/eslint-rules/{review-frontmatter.mjs → review-frontmatter.ts} +38 -16
- package/eslint-rules/rule-context.ts +62 -0
- package/eslint-rules/{sibling-frontmatter.mjs → sibling-frontmatter.ts} +1 -1
- package/eslint-rules/temp-root-realpath.harness.mjs +1 -1
- package/eslint-rules/{temp-root-realpath.mjs → temp-root-realpath.ts} +20 -7
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/{tex-build.mjs → tex-build.ts} +6 -5
- package/eslint.config.mjs +146 -30
- package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
- package/fixtures/install-path-literals/clean.md +2 -2
- package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
- package/fixtures/install-path-literals/defect.md +2 -2
- package/fixtures/pdf-facts/README.md +2 -2
- package/fixtures/real-markdown-paper/{baseline.mjs → baseline.ts} +31 -10
- package/hooks/{hooks.harness.mjs → hooks.harness.ts} +51 -34
- package/hooks/paper-edit-guard.hook.mjs +1 -1
- package/hooks/{paper-edit-guard.test.mjs → paper-edit-guard.test.ts} +3 -7
- package/hooks/paper-skills-nudge.hook.mjs +1 -1
- package/hooks/{paper-skills-nudge.test.mjs → paper-skills-nudge.test.ts} +2 -6
- package/hooks/paper-status-gates.sh +9 -19
- package/hooks/{paper-status-gates.test.mjs → paper-status-gates.test.ts} +2 -6
- package/lib/agent-cli-version.harness.mjs +1 -1
- package/lib/{agent-cli-version.mjs → agent-cli-version.ts} +32 -18
- package/lib/check.test.ts +14 -9
- package/lib/{check.mjs → check.ts} +22 -9
- package/lib/markdown.test.mjs +7 -7
- package/lib/{markdown.mjs → markdown.ts} +68 -29
- package/lib/paper-config.harness.mjs +9 -9
- package/lib/{paper-config.mjs → paper-config.ts} +20 -19
- package/lib/skill-checks.mjs +24 -381
- package/lib/skill-checks.test.mjs +64 -116
- package/lib/skill-corpus.mjs +10 -251
- package/lib/skill-corpus.test.mjs +2 -13
- package/lib/skill-eval-kit.mjs +1 -1
- package/lib/skill-trigger-cases.mjs +1 -1
- package/package.json +42 -10
- package/scripts/{check.harness.mjs → check.harness.ts} +111 -10
- package/scripts/{check.test.mjs → check.test.ts} +13 -10
- package/scripts/{check.mjs → check.ts} +116 -42
- package/scripts/coverage-config.test.ts +80 -38
- package/scripts/eslint-report-guard.test.mjs +1 -1
- package/scripts/harness-api.frozen.json +14 -16
- package/scripts/harness-api.test.ts +8 -5
- package/scripts/layer-legacy-frozen.test.ts +17 -13
- package/scripts/{layer-legacy-frozen.mjs → layer-legacy-frozen.ts} +128 -39
- package/scripts/release-config.test.ts +12 -4
- package/scripts/rules-see-files.test.mjs +1 -1
- package/scripts/test.test.ts +78 -0
- package/scripts/test.ts +87 -0
- package/skills/analyze-sibling-paper/SKILL.md +1 -39
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
- package/skills/argument-arc/SKILL.md +2 -36
- package/skills/argument-arc/SKILL.md.spec.ts +1 -43
- package/skills/build-benchmark/SKILL.md +1 -41
- package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
- package/skills/camera-ready/SKILL.md +1 -34
- package/skills/camera-ready/SKILL.md.spec.ts +0 -33
- package/skills/cold-read-diff/SKILL.md +1 -35
- package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
- package/skills/draft-paper/SKILL.md +2 -41
- package/skills/draft-paper/SKILL.md.spec.ts +1 -50
- package/skills/extend-paper/SKILL.md +2 -33
- package/skills/extend-paper/SKILL.md.spec.ts +1 -42
- package/skills/find-venue/SKILL.md +2 -38
- package/skills/find-venue/SKILL.md.spec.ts +1 -47
- package/skills/grade-paper-writing/SKILL.md +3 -38
- package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
- package/skills/grade-paper-writing/prose-lint.mjs +3 -4
- package/skills/grade-paper-writing/{prose-lint.test.mjs → prose-lint.test.ts} +3 -3
- package/skills/harden-paper/SKILL.md +1 -39
- package/skills/harden-paper/SKILL.md.spec.ts +0 -38
- package/skills/map-prior-work/SKILL.md +1 -35
- package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
- package/skills/osf-artifact-upload/{osf-artifact-upload.harness.mjs → osf-artifact-upload.harness.ts} +3 -4
- package/skills/paper-adversarial-review/SKILL.md +2 -35
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
- package/skills/paper-pipeline/SKILL.md +2 -28
- package/skills/paper-pipeline/SKILL.md.spec.ts +1 -27
- package/skills/paper-pipeline/description-language.eval.mjs +1 -1
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +1 -1
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +2 -2
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
- package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
- package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
- package/skills/paper-pipeline/scripts/README.md +30 -225
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +1 -1
- package/skills/paper-pipeline/scripts/{artifact-coverage.test.mjs → artifact-coverage.test.ts} +2 -2
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +6 -104
- package/skills/paper-pipeline/scripts/consumer.mjs +24 -109
- package/skills/paper-pipeline/scripts/consumer.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +1 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +2 -3
- package/skills/paper-pipeline/scripts/{generated-code.test.mjs → generated-code.test.ts} +3 -3
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
- package/skills/paper-pipeline/scripts/population-map.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/{round-diff.harness.mjs → round-diff.harness.ts} +45 -18
- package/skills/paper-pipeline/scripts/round-diff.mjs +1 -1
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +1 -1
- package/skills/paper-status/SKILL.md +2 -42
- package/skills/paper-status/SKILL.md.spec.ts +1 -41
- package/skills/pc-panel-review/SKILL.md +2 -46
- package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
- package/skills/plan-paper-timeline/SKILL.md +2 -36
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
- package/skills/plan-paper-timeline/fixtures/{fake-google-calendar.mjs → fake-google-calendar.ts} +101 -70
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +3 -3
- package/skills/render-paper/SKILL.md +1 -37
- package/skills/render-paper/SKILL.md.spec.ts +0 -36
- package/skills/render-paper/{extract-pdf-facts.harness.mjs → extract-pdf-facts.harness.ts} +34 -9
- package/skills/render-paper/render-paper.harness.mjs +2 -2
- package/skills/research-ideate/SKILL.md +2 -39
- package/skills/research-ideate/SKILL.md.spec.ts +1 -47
- package/skills/study-accepted-papers/SKILL.md +1 -34
- package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
- package/skills/submit-paper/SKILL.md +1 -33
- package/skills/submit-paper/SKILL.md.spec.ts +0 -32
- package/skills/sweep-design-space/SKILL.md +1 -38
- package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
- package/skills/tighten-paper/SKILL.md +2 -39
- package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
- package/skills/tighten-paper/structure.mjs +5 -12
- package/skills/tighten-paper/{structure.test.mjs → structure.test.ts} +4 -3
- package/skills/verify-citations/SKILL.md +2 -38
- package/skills/verify-citations/SKILL.md.spec.ts +1 -37
- package/skills/verify-citations/scripts/{bib-authors.cli.test.mjs → bib-authors.cli.test.ts} +14 -4
- package/skills/verify-citations/scripts/bib-authors.d.mts +63 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +1 -4
- package/skills/verify-citations/scripts/{verify-cites.cli.test.mjs → verify-cites.cli.test.ts} +32 -9
- package/skills/verify-citations/scripts/verify-cites.d.mts +58 -0
- package/src/CLAUDE.md +1 -1
- package/src/adapters/banal/failure.ts +9 -10
- package/src/adapters/banal/index.test.ts +5 -8
- package/src/adapters/banal/index.ts +8 -7
- package/src/adapters/banal/install.ts +9 -4
- package/src/adapters/banal/invocation.test.ts +23 -9
- package/src/adapters/banal/invocation.ts +24 -9
- package/src/adapters/banal/locate.test.ts +2 -2
- package/src/adapters/banal/locate.ts +27 -21
- package/src/adapters/banal/output.test.ts +1 -1
- package/src/adapters/banal/run.test.ts +5 -5
- package/src/adapters/banal/run.ts +16 -10
- package/src/adapters/banal/settings.ts +2 -2
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/curl/download.test.ts +8 -6
- package/src/adapters/memory/index.ts +4 -3
- package/src/adapters/node/files.io.ts +2 -2
- package/src/adapters/node/files.test.ts +8 -4
- package/src/adapters/node/process.io.ts +13 -5
- package/src/adapters/node/process.test.ts +5 -5
- package/src/adapters/node/workspace.io.ts +9 -4
- package/src/adapters/node/workspace.test.ts +3 -1
- package/src/adapters/pdfjs/fill.test.ts +2 -2
- package/src/adapters/pdfjs/fill.ts +14 -7
- package/src/adapters/references/index.test.ts +31 -38
- package/src/adapters/references/index.ts +33 -60
- package/src/adapters/references/pool.ts +3 -5
- package/src/adapters/references/reach.io.ts +2 -1
- package/src/build-engine.harness.mjs +1 -1
- package/src/build-engine.test.ts +13 -8
- package/src/build-engine.ts +12 -7
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +24 -24
- package/src/build.ts +34 -26
- package/src/caller-path.ts +13 -0
- package/src/cli.harness.mjs +6 -8
- package/src/cli.test.ts +31 -13
- package/src/cli.ts +117 -58
- package/src/doctor.harness.mjs +3 -3
- package/src/doctor.test.ts +1 -1
- package/src/doctor.ts +17 -7
- package/src/domain/lookup-cache.ts +21 -11
- package/src/domain/paths.test.ts +15 -0
- package/src/domain/paths.ts +20 -1
- package/src/domain/ready.ts +31 -0
- package/src/domain/record.test.ts +20 -0
- package/src/domain/record.ts +12 -0
- package/src/domain/result.test.ts +2 -2
- package/src/domain/sha256.ts +5 -3
- package/src/domain/text.test.ts +32 -0
- package/src/domain/text.ts +23 -0
- package/src/engine.harness.mjs +2 -2
- package/src/engine.ts +34 -5
- package/src/facts-file.test.ts +9 -7
- package/src/facts-file.ts +45 -29
- package/src/{hooks-settings.harness.mjs → hooks-settings.harness.ts} +56 -20
- package/src/hooks-settings.ts +48 -13
- package/src/init.test.ts +37 -14
- package/src/init.ts +48 -24
- package/src/{latex-log.harness.mjs → latex-log.harness.ts} +14 -14
- package/src/{latex-loop.harness.mjs → latex-loop.harness.ts} +64 -36
- package/src/latex-loop.ts +3 -3
- package/src/{link-skills.harness.mjs → link-skills.harness.ts} +19 -19
- package/src/link-skills.test.ts +3 -3
- package/src/link-skills.ts +5 -4
- package/src/lint-fix.test.ts +4 -6
- package/src/linted-set.test.ts +2 -2
- package/src/new-paper.harness.mjs +9 -25
- package/src/new-paper.test.ts +8 -10
- package/src/new-paper.ts +9 -5
- package/src/paper-files.test.ts +25 -24
- package/src/paper-files.ts +16 -27
- package/src/paper-settings-commands.test.ts +5 -4
- package/src/paper-settings.test.ts +7 -7
- package/src/paper-settings.ts +13 -14
- package/src/{pdf-facts.harness.mjs → pdf-facts.harness.ts} +9 -8
- package/src/pdf-facts.test.ts +25 -15
- package/src/pdf-facts.ts +81 -23
- package/src/{pdf-geometry.harness.mjs → pdf-geometry.harness.ts} +19 -12
- package/src/pdf-geometry.ts +8 -4
- package/src/ports/tool-installer.ts +2 -17
- package/src/presets.ts +31 -16
- package/src/references.test.ts +76 -62
- package/src/references.ts +18 -12
- package/src/{rules-config.harness.mjs → rules-config.harness.ts} +7 -13
- package/src/rules-config.ts +46 -14
- package/src/rules-off.test.ts +6 -4
- package/src/{structure.harness.mjs → structure.harness.ts} +26 -15
- package/src/structure.ts +1 -1
- package/src/{tex-requirements.harness.mjs → tex-requirements.harness.ts} +50 -22
- package/src/tex-requirements.test.ts +21 -0
- package/src/tex-requirements.ts +37 -11
- package/src/toolchain.harness.mjs +2 -2
- package/src/toolchain.test.ts +10 -6
- package/src/toolchain.ts +31 -17
- package/src/types.ts +5 -3
- package/src/venue-rules.test.ts +70 -68
- package/src/venue-rules.ts +22 -12
- package/tsconfig.json +6 -3
- package/tsconfig.pkg.json +10 -0
- package/lib/check.d.mts +0 -17
- package/lib/paper-config.d.mts +0 -31
- package/scripts/layer-legacy-frozen.d.mts +0 -36
- package/scripts/rules-are-content-only.harness.mjs +0 -105
- package/scripts/rules-are-content-only.mjs +0 -148
- package/scripts/rules-are-content-only.test.mjs +0 -61
- package/scripts/test.mjs +0 -44
- package/scripts/test.test.mjs +0 -52
- package/skills/paper-pipeline/scripts/announce.mjs +0 -67
- package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
- package/skills/paper-pipeline/scripts/fixtures/scorecard.mjs +0 -71
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
- package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
- package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -753
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
- package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
- package/skills/paper-pipeline/scripts/status.mjs +0 -295
- package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
- /package/bin/{paperlint.test.mjs → paperlint.test.ts} +0 -0
- /package/lib/{skill-eval-fixture.mjs → skill-eval-fixture.ts} +0 -0
- /package/skills/grade-paper-writing/__snapshots__/{prose-lint.test.mjs.snap → prose-lint.test.ts.snap} +0 -0
- /package/skills/tighten-paper/__snapshots__/{structure.test.mjs.snap → structure.test.ts.snap} +0 -0
|
@@ -20,7 +20,9 @@ import { afterAll as after, test } from "vitest";
|
|
|
20
20
|
import { tmpWorkspace } from "./workspace.io.ts";
|
|
21
21
|
|
|
22
22
|
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-ws-test-")));
|
|
23
|
-
after(() =>
|
|
23
|
+
after(() => {
|
|
24
|
+
rmSync(root, { recursive: true, force: true });
|
|
25
|
+
});
|
|
24
26
|
|
|
25
27
|
test("the directory exists inside the scope and is gone after it", () => {
|
|
26
28
|
const ws = tmpWorkspace(root);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `fill.ts` — what is recovered from pdf.js's exports for each text item: whether it is upright, and
|
|
3
3
|
* the fill it was drawn in (the operator-list walk). Operator lists here are written by hand; the
|
|
4
|
-
* real-PDF half runs in `pdf-facts.harness.
|
|
4
|
+
* real-PDF half runs in `pdf-facts.harness.ts` and `test/e2e/banal.ts`.
|
|
5
5
|
*/
|
|
6
6
|
import assert from "node:assert/strict";
|
|
7
7
|
import { test } from "vitest";
|
|
@@ -34,7 +34,7 @@ const OPS = {
|
|
|
34
34
|
showText: 5,
|
|
35
35
|
showSpacedText: 6,
|
|
36
36
|
};
|
|
37
|
-
const glyphs = (s: string) => [
|
|
37
|
+
const glyphs = (s: string) => [Array.from(s, (unicode) => ({ unicode }))];
|
|
38
38
|
const ops = (...pairs: [number, unknown?][]): OperatorList => ({
|
|
39
39
|
fnArray: pairs.map((p) => p[0]),
|
|
40
40
|
argsArray: pairs.map((p) => p[1] ?? null),
|
|
@@ -33,9 +33,12 @@ export interface OperatorList {
|
|
|
33
33
|
/** Text as the walk compares it: normalised, with no whitespace (items and glyphs split spaces differently). */
|
|
34
34
|
const squeeze = (s: string): string => s.normalize("NFKC").replace(/\s+/g, "");
|
|
35
35
|
|
|
36
|
+
/** An operator's argument list; pdf.js types it `any`, and a malformed one reads as empty. */
|
|
37
|
+
const listOf = (v: unknown): readonly unknown[] => (Array.isArray(v) ? v : []);
|
|
38
|
+
|
|
36
39
|
/** `setFillRGBColor`'s argument: a `#rrggbb` string in current pdf.js, three 0-255 numbers in older ones. */
|
|
37
40
|
export function hexOf(args: unknown): string {
|
|
38
|
-
const a =
|
|
41
|
+
const a = listOf(args);
|
|
39
42
|
if (typeof a[0] === "string") return a[0].toLowerCase();
|
|
40
43
|
const byte = (v: unknown): string =>
|
|
41
44
|
Math.round(typeof v === "number" ? v : 0)
|
|
@@ -46,12 +49,13 @@ export function hexOf(args: unknown): string {
|
|
|
46
49
|
|
|
47
50
|
/** The characters a show-text operator draws, from its glyph objects. */
|
|
48
51
|
function glyphText(args: unknown): string {
|
|
49
|
-
const
|
|
50
|
-
const glyphs = Array.isArray(a[0]) ? (a[0] as unknown[]) : [];
|
|
52
|
+
const glyphs = listOf(listOf(args)[0]);
|
|
51
53
|
return glyphs
|
|
52
54
|
.map((g) =>
|
|
53
55
|
typeof g === "object" && g !== null && "unicode" in g
|
|
54
|
-
?
|
|
56
|
+
? typeof g.unicode === "string"
|
|
57
|
+
? g.unicode
|
|
58
|
+
: ""
|
|
55
59
|
: "",
|
|
56
60
|
)
|
|
57
61
|
.join("");
|
|
@@ -76,7 +80,7 @@ function applyState(
|
|
|
76
80
|
else if (fn === ops.setFillRGBColor)
|
|
77
81
|
s.cur.fill = { kind: "rgb", hex: hexOf(args) };
|
|
78
82
|
else if (fn === ops.setTextRenderingMode)
|
|
79
|
-
s.cur.mode = Number((args
|
|
83
|
+
s.cur.mode = Number(listOf(args)[0] ?? 0);
|
|
80
84
|
else return false;
|
|
81
85
|
return true;
|
|
82
86
|
}
|
|
@@ -89,7 +93,10 @@ interface Drawn {
|
|
|
89
93
|
|
|
90
94
|
/** Every drawn character, in content order, with the fill it was drawn in. */
|
|
91
95
|
export function drawnCharacters(ops: ColourOps, list: OperatorList): Drawn[] {
|
|
92
|
-
const s
|
|
96
|
+
const s: { cur: State; stack: State[] } = {
|
|
97
|
+
cur: { fill: { kind: "unknown" }, mode: 0 },
|
|
98
|
+
stack: [],
|
|
99
|
+
};
|
|
93
100
|
const drawn: Drawn[] = [];
|
|
94
101
|
list.fnArray.forEach((fn, i) => {
|
|
95
102
|
const args = list.argsArray[i];
|
|
@@ -149,7 +156,7 @@ export function fillsFor<T>(
|
|
|
149
156
|
const drawn = drawnCharacters(ops, list);
|
|
150
157
|
let at = 0;
|
|
151
158
|
const fillOf = (text: string): Fill => {
|
|
152
|
-
const want =
|
|
159
|
+
const want = Array.from(squeeze(text));
|
|
153
160
|
if (want.length === 0) return { kind: "unknown" };
|
|
154
161
|
const hit = findRun(drawn, want, at);
|
|
155
162
|
if (hit === null) return { kind: "unknown" };
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
EMPTY_LOOKUP_CACHE,
|
|
11
11
|
type LookupCache,
|
|
12
12
|
} from "../../domain/lookup-cache.ts";
|
|
13
|
-
// @ts-expect-error — a skill script in .mjs, it has no types
|
|
14
13
|
import * as cites from "../../../skills/verify-citations/scripts/verify-cites.mjs";
|
|
15
14
|
|
|
16
15
|
/** The verdicts of a cold run — an empty cache, the adapter the CLI wires. */
|
|
@@ -46,29 +45,29 @@ const dblp = (title: string, authors: string[]) =>
|
|
|
46
45
|
/** Route by URL prefix; the longest prefix wins. */
|
|
47
46
|
function fakeFetch(routes: Record<string, () => Response>): string[] {
|
|
48
47
|
const calls: string[] = [];
|
|
49
|
-
vi.stubGlobal("fetch",
|
|
48
|
+
vi.stubGlobal("fetch", (url: string) => {
|
|
50
49
|
calls.push(url);
|
|
51
|
-
const hit = Object.
|
|
52
|
-
.filter((p) => url.startsWith(p))
|
|
53
|
-
.sort((a, b) => b.length - a.length)[0];
|
|
54
|
-
|
|
55
|
-
return routes[hit]!();
|
|
50
|
+
const hit = Object.entries(routes)
|
|
51
|
+
.filter(([p]) => url.startsWith(p))
|
|
52
|
+
.sort(([a], [b]) => b.length - a.length)[0];
|
|
53
|
+
return Promise.resolve(hit === undefined ? json(404) : hit[1]());
|
|
56
54
|
});
|
|
57
55
|
return calls;
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
/** Await `p` while fake time runs, so DBLP's pauses between requests elapse at once. */
|
|
61
59
|
async function settle<T>(p: Promise<T>): Promise<T> {
|
|
62
|
-
let
|
|
63
|
-
const
|
|
64
|
-
|
|
60
|
+
// A field, not a `let`: the flag is set in a callback, which flow analysis cannot see.
|
|
61
|
+
const state = { done: false };
|
|
62
|
+
const out = p.finally(() => (state.done = true));
|
|
63
|
+
while (!state.done) await vi.advanceTimersByTimeAsync(1000);
|
|
65
64
|
return out;
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
test("no service answers: not-checked, with the reason", async () => {
|
|
69
|
-
vi.stubGlobal("fetch",
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
vi.stubGlobal("fetch", () =>
|
|
69
|
+
Promise.reject(new Error("getaddrinfo ENOTFOUND api.crossref.org")),
|
|
70
|
+
);
|
|
72
71
|
assert.deepEqual(await checkOnly("@misc{k, title={T}}"), {
|
|
73
72
|
kind: "not-checked",
|
|
74
73
|
why: "the citation services cannot be reached (getaddrinfo ENOTFOUND api.crossref.org)",
|
|
@@ -104,19 +103,19 @@ test("each entry gets its existence and its authors: confirmed, fabricated, mism
|
|
|
104
103
|
const r = await settle(checkOnly(bib));
|
|
105
104
|
assert.equal(r.kind, "checked");
|
|
106
105
|
const byKey = Object.fromEntries(
|
|
107
|
-
|
|
108
|
-
e.key,
|
|
109
|
-
[e.exists, e.authors, e.why ?? ""],
|
|
110
|
-
]),
|
|
106
|
+
r.entries.map((e) => [e.key, [e.exists, e.authors, e.why ?? ""]]),
|
|
111
107
|
);
|
|
112
108
|
assert.deepEqual(
|
|
113
109
|
{
|
|
114
|
-
good: byKey["good"]
|
|
115
|
-
fake: byKey["fake"]
|
|
116
|
-
drift: byKey["drift"]
|
|
117
|
-
driftWhy: String(byKey["drift"]
|
|
118
|
-
flaky: [
|
|
119
|
-
|
|
110
|
+
good: byKey["good"]?.slice(0, 2),
|
|
111
|
+
fake: byKey["fake"]?.slice(0, 2),
|
|
112
|
+
drift: byKey["drift"]?.slice(0, 2),
|
|
113
|
+
driftWhy: String(byKey["drift"]?.[2]).split("; ").pop(),
|
|
114
|
+
flaky: [
|
|
115
|
+
byKey["flaky"]?.[1],
|
|
116
|
+
String(byKey["flaky"]?.[2]).split("; ").pop(),
|
|
117
|
+
],
|
|
118
|
+
pre: byKey["pre"]?.slice(0, 2),
|
|
120
119
|
},
|
|
121
120
|
{
|
|
122
121
|
good: ["true", "match"],
|
|
@@ -209,20 +208,14 @@ test(
|
|
|
209
208
|
slowFetch(40);
|
|
210
209
|
const bib = manyBib(20);
|
|
211
210
|
const serial: string[] = [];
|
|
212
|
-
for (const c of cites.parseBib(bib)
|
|
213
|
-
serial.push(
|
|
214
|
-
(
|
|
215
|
-
(await cites.verifyCitationLive(c, { cache: {} })) as {
|
|
216
|
-
verdict: string;
|
|
217
|
-
}
|
|
218
|
-
).verdict,
|
|
219
|
-
);
|
|
211
|
+
for (const c of cites.parseBib(bib))
|
|
212
|
+
serial.push((await cites.verifyCitationLive(c, { cache: {} })).verdict);
|
|
220
213
|
const t0 = Date.now();
|
|
221
214
|
const r = await checkOnly(bib);
|
|
222
215
|
const ms = Date.now() - t0;
|
|
223
216
|
assert.equal(r.kind, "checked");
|
|
224
217
|
assert.deepEqual(
|
|
225
|
-
r.
|
|
218
|
+
r.entries.map((e) => e.exists),
|
|
226
219
|
serial,
|
|
227
220
|
);
|
|
228
221
|
assert.ok(ms < (20 * 200) / 2, `${String(ms)} ms for 20 lookups`);
|
|
@@ -249,7 +242,7 @@ const PAPERS = [
|
|
|
249
242
|
"@inproceedings{good, author={Ada Lovelace}, title={Good Paper}, booktitle={ICSE}, doi={10.1/good}}",
|
|
250
243
|
"@inproceedings{other, author={Ada Lovelace}, title={Other Paper}, booktitle={ICSE}}",
|
|
251
244
|
"@misc{pre, author={Ada Lovelace}, title={A Preprint}, journal={arXiv preprint}}",
|
|
252
|
-
];
|
|
245
|
+
] as const;
|
|
253
246
|
/** Every service answers; DBLP knows "Good Paper" and nothing else. */
|
|
254
247
|
const answering = () =>
|
|
255
248
|
fakeFetch({
|
|
@@ -414,7 +407,7 @@ test("a failed lookup is not cached: the next run asks it again", async () => {
|
|
|
414
407
|
"https://api.crossref.org/": () => json(200, { message: { items: [] } }),
|
|
415
408
|
"https://dblp.org/": () => json(500),
|
|
416
409
|
});
|
|
417
|
-
const bib = PAPERS[1]
|
|
410
|
+
const bib = PAPERS[1];
|
|
418
411
|
const r = await settle(cached(bib, EMPTY_LOOKUP_CACHE));
|
|
419
412
|
const failed: LookupCache = r.cache;
|
|
420
413
|
assert.deepEqual(
|
|
@@ -427,10 +420,10 @@ test("a failed lookup is not cached: the next run asks it again", async () => {
|
|
|
427
420
|
});
|
|
428
421
|
|
|
429
422
|
test("offline with an incomplete cache: not-checked, and the cache comes back unchanged", async () => {
|
|
430
|
-
vi.stubGlobal("fetch",
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const r = await cached(PAPERS[0]
|
|
423
|
+
vi.stubGlobal("fetch", () =>
|
|
424
|
+
Promise.reject(new Error("getaddrinfo ENOTFOUND api.crossref.org")),
|
|
425
|
+
);
|
|
426
|
+
const r = await cached(PAPERS[0], EMPTY_LOOKUP_CACHE);
|
|
434
427
|
assert.deepEqual(r, {
|
|
435
428
|
check: {
|
|
436
429
|
kind: "not-checked",
|
|
@@ -35,29 +35,16 @@ import { sleep, todayUtc } from "./clock.io.ts";
|
|
|
35
35
|
|
|
36
36
|
/** Citations looked up at once (#107). */
|
|
37
37
|
const LOOKUPS_IN_FLIGHT = 6;
|
|
38
|
-
// @ts-expect-error — a skill script in .mjs, it has no types
|
|
39
38
|
import * as cites from "../../../skills/verify-citations/scripts/verify-cites.mjs";
|
|
40
|
-
|
|
39
|
+
import type { CiteVerdict } from "../../../skills/verify-citations/scripts/verify-cites.mjs";
|
|
41
40
|
import * as authors from "../../../skills/verify-citations/scripts/bib-authors.mjs";
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
interface AuthorFinding {
|
|
51
|
-
readonly key: string;
|
|
52
|
-
readonly venue: string;
|
|
53
|
-
readonly missing: readonly string[];
|
|
54
|
-
readonly extra: readonly string[];
|
|
55
|
-
readonly orderDiffers: boolean;
|
|
56
|
-
}
|
|
57
|
-
interface KeyWhy {
|
|
58
|
-
readonly key: string;
|
|
59
|
-
readonly why: string;
|
|
60
|
-
}
|
|
41
|
+
import type {
|
|
42
|
+
AuthorBuckets,
|
|
43
|
+
AuthorFinding,
|
|
44
|
+
BibEntry,
|
|
45
|
+
} from "../../../skills/verify-citations/scripts/bib-authors.mjs";
|
|
46
|
+
import { messageOf } from "../../domain/text.ts";
|
|
47
|
+
import { isRecord } from "../../domain/record.ts";
|
|
61
48
|
|
|
62
49
|
const describeAuthors = (f: AuthorFinding): string =>
|
|
63
50
|
[
|
|
@@ -68,13 +55,6 @@ const describeAuthors = (f: AuthorFinding): string =>
|
|
|
68
55
|
.filter(Boolean)
|
|
69
56
|
.join("; ") + ` (DBLP: ${f.venue})`;
|
|
70
57
|
|
|
71
|
-
interface AuthorBuckets {
|
|
72
|
-
readonly findings: readonly AuthorFinding[];
|
|
73
|
-
readonly skipped: readonly KeyWhy[];
|
|
74
|
-
readonly unchecked: readonly KeyWhy[];
|
|
75
|
-
readonly matched: readonly string[];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
58
|
const authorsOf = (key: string, a: AuthorBuckets): EntryVerdict["authors"] =>
|
|
79
59
|
a.findings.some((f) => f.key === key)
|
|
80
60
|
? "mismatch"
|
|
@@ -90,7 +70,7 @@ const authorsOf = (key: string, a: AuthorBuckets): EntryVerdict["authors"] =>
|
|
|
90
70
|
* confirmed work: a refusal after a confirmation changed nothing.
|
|
91
71
|
*/
|
|
92
72
|
const unconfirmed = (
|
|
93
|
-
c:
|
|
73
|
+
c: CiteVerdict | undefined,
|
|
94
74
|
): readonly (string | undefined)[] =>
|
|
95
75
|
c && c.verdict !== "true"
|
|
96
76
|
? [c.reason, ...(c.refused ?? []).map((r) => `not asked: ${r}`)]
|
|
@@ -99,7 +79,7 @@ const unconfirmed = (
|
|
|
99
79
|
/** One entry's verdict from the two checkers' answers. */
|
|
100
80
|
function entryVerdict(
|
|
101
81
|
key: string,
|
|
102
|
-
found: readonly
|
|
82
|
+
found: readonly CiteVerdict[],
|
|
103
83
|
a: AuthorBuckets,
|
|
104
84
|
): EntryVerdict {
|
|
105
85
|
const c = found.find((x) => x.id === key);
|
|
@@ -119,27 +99,21 @@ function entryVerdict(
|
|
|
119
99
|
};
|
|
120
100
|
}
|
|
121
101
|
|
|
122
|
-
interface BibAuthorsEntry {
|
|
123
|
-
readonly key: string;
|
|
124
|
-
readonly title: string;
|
|
125
|
-
readonly author: string;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
102
|
/** Whether bib-authors will ask DBLP about this entry — the filter its `checkAuthors` applies. */
|
|
129
|
-
const asksDblp = (e:
|
|
103
|
+
const asksDblp = (e: BibEntry): boolean =>
|
|
130
104
|
Boolean(e.title && e.author) &&
|
|
131
|
-
|
|
132
|
-
!
|
|
105
|
+
authors.claimsPublished(e) &&
|
|
106
|
+
!authors.truncated(e.author);
|
|
133
107
|
|
|
134
108
|
/** Is every question this bibliography would ask already answered in `cache`? */
|
|
135
109
|
async function fullyCached(
|
|
136
|
-
citations: readonly
|
|
137
|
-
entries: readonly
|
|
110
|
+
citations: readonly cites.Citation[],
|
|
111
|
+
entries: readonly BibEntry[],
|
|
138
112
|
cache: LookupCache,
|
|
139
113
|
store: Readonly<Record<string, unknown>>,
|
|
140
114
|
): Promise<boolean> {
|
|
141
115
|
const asks = await Promise.all(
|
|
142
|
-
citations.map((c) => cites.wouldAsk(c, store)
|
|
116
|
+
citations.map((c) => cites.wouldAsk(c, store)),
|
|
143
117
|
);
|
|
144
118
|
return (
|
|
145
119
|
!asks.includes(true) &&
|
|
@@ -185,11 +159,11 @@ function cachedDblp(cache: LookupCache, today: () => string) {
|
|
|
185
159
|
if (refused !== null)
|
|
186
160
|
throw new Error(`DBLP refused earlier in this run (${refused})`);
|
|
187
161
|
try {
|
|
188
|
-
const hits =
|
|
162
|
+
const hits = await authors.dblpHits(title);
|
|
189
163
|
dblp.set(dblpTitleKey(title), { fetched: today(), title, hits });
|
|
190
164
|
return hits;
|
|
191
165
|
} catch (e) {
|
|
192
|
-
refused = (e
|
|
166
|
+
refused = messageOf(e);
|
|
193
167
|
throw new Error(`DBLP refused earlier in this run (${refused})`);
|
|
194
168
|
}
|
|
195
169
|
},
|
|
@@ -215,12 +189,16 @@ function grown(
|
|
|
215
189
|
): LookupCache {
|
|
216
190
|
const { cache, usable } = loaded;
|
|
217
191
|
const { store, dblp } = run;
|
|
218
|
-
|
|
192
|
+
// verify-cites stores each answer as an object; only such a value is a response the cache can hold.
|
|
193
|
+
const fresh = Object.entries(store).filter(
|
|
194
|
+
(e): e is [string, Readonly<Record<string, unknown>>] =>
|
|
195
|
+
!usable.citations.has(e[0]) && isRecord(e[1]),
|
|
196
|
+
);
|
|
219
197
|
const freshDblp = [...dblp].filter(([k]) => !usable.dblp.has(k));
|
|
220
198
|
if (fresh.length === 0 && freshDblp.length === 0) return cache;
|
|
221
199
|
const dated = fresh.map(([k, response]): [string, CachedResponse] => [
|
|
222
200
|
k,
|
|
223
|
-
{ fetched: today(), response
|
|
201
|
+
{ fetched: today(), response },
|
|
224
202
|
]);
|
|
225
203
|
return {
|
|
226
204
|
citations: new Map([...cache.citations, ...dated]),
|
|
@@ -234,10 +212,8 @@ export const referencesChecker =
|
|
|
234
212
|
async (bib, cache) => {
|
|
235
213
|
// Answers past MAX_AGE_DAYS are left out, so the run asks them again (and `grown` re-dates them).
|
|
236
214
|
const usable = freshPart(cache, today());
|
|
237
|
-
const citations =
|
|
238
|
-
|
|
239
|
-
);
|
|
240
|
-
const parsed = authors.parseBib(bib) as BibAuthorsEntry[];
|
|
215
|
+
const citations = cites.parseBib(bib);
|
|
216
|
+
const parsed = authors.parseBib(bib);
|
|
241
217
|
const store: Record<string, unknown> = Object.fromEntries(
|
|
242
218
|
[...usable.citations].map(([k, v]) => [k, v.response]),
|
|
243
219
|
);
|
|
@@ -252,22 +228,19 @@ export const referencesChecker =
|
|
|
252
228
|
// answer.
|
|
253
229
|
// One breaker for the run: a service that refuses is not asked again, not once per citation
|
|
254
230
|
// (#120). Requests already in flight when it starts refusing still land — at most six.
|
|
255
|
-
const breaker = cites.createBreaker()
|
|
231
|
+
const breaker = cites.createBreaker();
|
|
256
232
|
const d = cachedDblp(usable, today);
|
|
257
233
|
const [found, a] = await Promise.all([
|
|
258
|
-
mapLimit(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
cache: store,
|
|
264
|
-
breaker,
|
|
265
|
-
}) as Promise<CiteResult>,
|
|
234
|
+
mapLimit(citations, LOOKUPS_IN_FLIGHT, (c) =>
|
|
235
|
+
cites.verifyCitationLive(c, {
|
|
236
|
+
cache: store,
|
|
237
|
+
breaker,
|
|
238
|
+
}),
|
|
266
239
|
),
|
|
267
240
|
authors.checkAuthors(parsed, {
|
|
268
241
|
lookup: d.lookup,
|
|
269
242
|
pause: d.pause,
|
|
270
|
-
})
|
|
243
|
+
}),
|
|
271
244
|
]);
|
|
272
245
|
const keys = new Set([
|
|
273
246
|
...found.map((c) => c.id),
|
|
@@ -10,12 +10,10 @@ export async function mapLimit<T, R>(
|
|
|
10
10
|
fn: (item: T) => Promise<R>,
|
|
11
11
|
): Promise<R[]> {
|
|
12
12
|
const out: R[] = new Array<R>(items.length);
|
|
13
|
-
|
|
13
|
+
// ONE iterator shared by every worker: each `next()` hands out the next index exactly once.
|
|
14
|
+
const queue = items.entries();
|
|
14
15
|
const worker = async (): Promise<void> => {
|
|
15
|
-
|
|
16
|
-
const i = next++;
|
|
17
|
-
out[i] = await fn(items[i] as T);
|
|
18
|
-
}
|
|
16
|
+
for (const [i, item] of queue) out[i] = await fn(item);
|
|
19
17
|
};
|
|
20
18
|
await Promise.all(
|
|
21
19
|
Array.from({ length: Math.min(limit, items.length) }, worker),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messageOf } from "../../domain/text.ts";
|
|
1
2
|
/** Whether the citation services answer at all — the one request that decides "not checked". */
|
|
2
3
|
export async function unreachable(): Promise<string | null> {
|
|
3
4
|
try {
|
|
@@ -7,6 +8,6 @@ export async function unreachable(): Promise<string | null> {
|
|
|
7
8
|
});
|
|
8
9
|
return null;
|
|
9
10
|
} catch (e) {
|
|
10
|
-
return `the citation services cannot be reached (${(e
|
|
11
|
+
return `the citation services cannot be reached (${messageOf(e)})`;
|
|
11
12
|
}
|
|
12
13
|
}
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import { tmpdir } from "node:os";
|
|
20
20
|
import { basename, delimiter, dirname, join } from "node:path";
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
|
-
import { createChecker } from "../lib/check.
|
|
22
|
+
import { createChecker } from "../lib/check.ts";
|
|
23
23
|
|
|
24
24
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
const E = await import(join(HERE, "build-engine.ts"));
|
package/src/build-engine.test.ts
CHANGED
|
@@ -7,8 +7,9 @@ import assert from "node:assert/strict";
|
|
|
7
7
|
import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { dirname, join } from "node:path";
|
|
9
9
|
import { test } from "vitest";
|
|
10
|
-
import { useTempDir } from "../test/support.
|
|
10
|
+
import { useTempDir } from "../test/support.ts";
|
|
11
11
|
import { prepareEngine, refusal } from "./build-engine.ts";
|
|
12
|
+
import type { Runner } from "./engine.ts";
|
|
12
13
|
|
|
13
14
|
const work = useTempDir("paperlint-build-engine-test-");
|
|
14
15
|
const TEX = { packages: { acmart: ["acmart.cls"] }, tools: {} };
|
|
@@ -20,11 +21,12 @@ const texAt = (dir: string): string => {
|
|
|
20
21
|
return dir;
|
|
21
22
|
};
|
|
22
23
|
/** kpsewhich answers per bin directory. */
|
|
23
|
-
const fakeRun =
|
|
24
|
-
(
|
|
24
|
+
const fakeRun =
|
|
25
|
+
(found: Record<string, string>): Runner =>
|
|
26
|
+
(cmd) => ({
|
|
25
27
|
stdout: found[dirname(cmd)] ?? "",
|
|
26
28
|
status: 0,
|
|
27
|
-
})
|
|
29
|
+
});
|
|
28
30
|
|
|
29
31
|
async function go(o: Parameters<typeof prepareEngine>[0]) {
|
|
30
32
|
const out: string[] = [];
|
|
@@ -34,7 +36,7 @@ async function go(o: Parameters<typeof prepareEngine>[0]) {
|
|
|
34
36
|
err: (l) => err.push(l),
|
|
35
37
|
platform: "linux",
|
|
36
38
|
interactive: true,
|
|
37
|
-
ask:
|
|
39
|
+
ask: () => Promise.resolve("y"),
|
|
38
40
|
...o,
|
|
39
41
|
});
|
|
40
42
|
return { r, out, err };
|
|
@@ -101,7 +103,10 @@ test("a complete cache is used from an environment with no PATH at all", async (
|
|
|
101
103
|
|
|
102
104
|
test("the refusal line for a decision to use a tree names nothing missing", () => {
|
|
103
105
|
assert.equal(
|
|
104
|
-
refusal(
|
|
106
|
+
refusal(
|
|
107
|
+
{ kind: "use-cache", tree: { label: "cache", bin: "/b", missing: [] } },
|
|
108
|
+
{ system: null },
|
|
109
|
+
),
|
|
105
110
|
"✗ paperlint build: no TeX Live with every package these papers need — run `npx paperlint toolchain` (missing: 0 package(s): )",
|
|
106
111
|
);
|
|
107
112
|
});
|
|
@@ -122,14 +127,14 @@ test("without an ask or an install given: Enter is yes, and the default install
|
|
|
122
127
|
PAPERLINT_CTAN_MIRROR: "https://mirror.invalid/tlnet",
|
|
123
128
|
PATH: join(work, "nothing"),
|
|
124
129
|
},
|
|
125
|
-
run: (
|
|
130
|
+
run: (cmd) => {
|
|
126
131
|
calls.push(cmd.split("/").pop() ?? cmd);
|
|
127
132
|
return {
|
|
128
133
|
status: 22,
|
|
129
134
|
stdout: "",
|
|
130
135
|
stderr: "curl: (6) could not resolve\n",
|
|
131
136
|
};
|
|
132
|
-
}
|
|
137
|
+
},
|
|
133
138
|
});
|
|
134
139
|
assert.deepEqual(r, { ok: false, code: 1 });
|
|
135
140
|
assert.ok(calls.includes("curl"), calls.join(","));
|
package/src/build-engine.ts
CHANGED
|
@@ -62,16 +62,18 @@ type Resolved = Required<Omit<EngineOptions, "install">> & {
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
/** The options a caller actually set — `undefined` must still get its default, so no plain spread. */
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
65
|
+
function defined<T extends object>(o: T): Partial<T> {
|
|
66
|
+
const out: Partial<T> = {};
|
|
67
|
+
// `for…in` hands out `o`'s keys typed as its keys; an options literal has no inherited ones.
|
|
68
|
+
for (const k in o) if (o[k] !== undefined) out[k] = o[k];
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
69
71
|
|
|
70
72
|
function withDefaults(o: EngineOptions): Resolved {
|
|
71
73
|
const r = {
|
|
72
74
|
dryRun: false,
|
|
73
75
|
interactive: false,
|
|
74
|
-
ask:
|
|
76
|
+
ask: () => Promise.resolve(""),
|
|
75
77
|
log: console.log,
|
|
76
78
|
err: console.error,
|
|
77
79
|
// eslint-disable-next-line no-restricted-globals -- legacy I/O, moves behind a port in #76
|
|
@@ -130,10 +132,13 @@ export function gatherFacts(o: Resolved): EngineFacts {
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
const names = (missing: readonly string[]): string =>
|
|
133
|
-
`${missing.length} package(s): ${missing.join(", ")}`;
|
|
135
|
+
`${String(missing.length)} package(s): ${missing.join(", ")}`;
|
|
134
136
|
|
|
135
137
|
/** The one line a run without a human gets. It names the command and what is missing. */
|
|
136
|
-
export function refusal(
|
|
138
|
+
export function refusal(
|
|
139
|
+
d: EngineDecision,
|
|
140
|
+
facts: Pick<EngineFacts, "system">,
|
|
141
|
+
): string {
|
|
137
142
|
const missing = d.kind === "refuse" || d.kind === "ask" ? d.missing : [];
|
|
138
143
|
const sys =
|
|
139
144
|
facts.system && facts.system.missing.length
|
package/src/build.harness.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The process runner is replaced by a fake that writes what pdflatex and bibtex would write, so
|
|
5
5
|
* these assertions check the SHELL's decisions: what is run, with which environment, in which
|
|
6
6
|
* order, and what is left on disk. Whether a real pdflatex produces a real PDF is the other half,
|
|
7
|
-
* `test/e2e/build.
|
|
7
|
+
* `test/e2e/build.ts`.
|
|
8
8
|
*
|
|
9
9
|
* 🔴 THE TWO THINGS PINNED DOWN HERE THAT A RETURNED OBJECT CANNOT SHOW: a paper-supplied
|
|
10
10
|
* `build.sh` is never executed (checked by the trace it would leave on disk and by the list of
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
import { tmpdir } from "node:os";
|
|
23
23
|
import { join, dirname, delimiter } from "node:path";
|
|
24
24
|
import { fileURLToPath } from "node:url";
|
|
25
|
-
import { createChecker } from "../lib/check.
|
|
25
|
+
import { createChecker } from "../lib/check.ts";
|
|
26
26
|
|
|
27
27
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
28
28
|
const {
|
package/src/build.test.ts
CHANGED
|
@@ -8,7 +8,7 @@ import assert from "node:assert/strict";
|
|
|
8
8
|
import { existsSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { test } from "vitest";
|
|
11
|
-
import { useTempDir, writeTree } from "../test/support.
|
|
11
|
+
import { present, useTempDir, writeTree } from "../test/support.ts";
|
|
12
12
|
import {
|
|
13
13
|
bibInput,
|
|
14
14
|
buildPaper,
|
|
@@ -62,18 +62,18 @@ function bibtexRun(f: Fake, cwd: string) {
|
|
|
62
62
|
? { status: f.bibtex }
|
|
63
63
|
: { status: f.bibtex, stdout: f.bibtexOut };
|
|
64
64
|
}
|
|
65
|
-
function fakeRun(f: Fake): BuildOptions["run"] {
|
|
65
|
+
function fakeRun(f: Fake): NonNullable<BuildOptions["run"]> {
|
|
66
66
|
let pass = 0;
|
|
67
|
-
return (
|
|
67
|
+
return (bin, _args, opts) => {
|
|
68
68
|
if (bin === f.missing)
|
|
69
69
|
return {
|
|
70
70
|
error: Object.assign(new Error("spawn ENOENT"), { code: "ENOENT" }),
|
|
71
71
|
status: null,
|
|
72
72
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
73
|
+
// The build always runs a pass in the paper's directory.
|
|
74
|
+
const cwd = present(opts.cwd, "the pass's cwd");
|
|
75
|
+
return bin === "pdflatex" ? latexPass(f, cwd, ++pass) : bibtexRun(f, cwd);
|
|
76
|
+
};
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const NO_GEOMETRY: Geometry = {
|
|
@@ -103,18 +103,19 @@ const page = (words: number, numbered = 0) => ({
|
|
|
103
103
|
});
|
|
104
104
|
const reader =
|
|
105
105
|
(last = page(80)): PdfReader =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
() =>
|
|
107
|
+
Promise.resolve({
|
|
108
|
+
ok: true,
|
|
109
|
+
facts: {
|
|
110
|
+
pages: 1,
|
|
111
|
+
fonts: {
|
|
112
|
+
kind: "drawn",
|
|
113
|
+
list: [{ kind: "embedded", name: "CMR10", program: "Type1" }],
|
|
114
|
+
},
|
|
115
|
+
last,
|
|
116
|
+
layout: [],
|
|
113
117
|
},
|
|
114
|
-
|
|
115
|
-
layout: [],
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
+
});
|
|
118
119
|
const quiet = () => {};
|
|
119
120
|
/** buildPaper with fakes; the verdict line is what the command would print for it. */
|
|
120
121
|
async function build(dir: string, f: Fake = {}, over: BuildOptions = {}) {
|
|
@@ -145,11 +146,10 @@ test("documentclass: an empty name is no class; a macro inside the name contribu
|
|
|
145
146
|
// `\documentclass` came back bare has no name to read, and is no class.
|
|
146
147
|
assert.equal(
|
|
147
148
|
parseDocumentclass("\\documentclass", {
|
|
148
|
-
parse: () =>
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}) as never,
|
|
149
|
+
parse: () => ({
|
|
150
|
+
type: "root",
|
|
151
|
+
content: [{ type: "macro", content: "documentclass" }],
|
|
152
|
+
}),
|
|
153
153
|
}),
|
|
154
154
|
null,
|
|
155
155
|
);
|
|
@@ -332,7 +332,7 @@ test("paths: a paper that IS the working directory is named by its full path", a
|
|
|
332
332
|
const run = await buildPapers([dir], {
|
|
333
333
|
cwd: dir,
|
|
334
334
|
log: (l) => lines.push(l),
|
|
335
|
-
engine:
|
|
335
|
+
engine: () => Promise.resolve(null),
|
|
336
336
|
});
|
|
337
337
|
assert.deepEqual(
|
|
338
338
|
[run, lines],
|