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
|
@@ -14,7 +14,8 @@ import type { Geometry, Provenance } from "../../domain/geometry.ts";
|
|
|
14
14
|
import type { AbsolutePath } from "../../domain/paths.ts";
|
|
15
15
|
import { err, ok } from "../../domain/result.ts";
|
|
16
16
|
import type { MeasureGeometry } from "../../ports/measure-geometry.ts";
|
|
17
|
-
import
|
|
17
|
+
import { ready, type Ready } from "../../domain/ready.ts";
|
|
18
|
+
import type { ToolInstaller } from "../../ports/tool-installer.ts";
|
|
18
19
|
import { describe } from "./failure.ts";
|
|
19
20
|
import { provenanceLabel, type BanalCandidate } from "./locate.ts";
|
|
20
21
|
import { pinLabel, type BanalSource } from "./pin.ts";
|
|
@@ -65,12 +66,12 @@ export function banalMeasurer(
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
/** The one place banal's `Ready` is minted: from an `Installed`, whose `PinnedBanal` only run.ts mints. */
|
|
68
|
-
const
|
|
69
|
-
({
|
|
70
|
-
where: i.banal.path
|
|
69
|
+
const readyOf = (i: Installed): Ready =>
|
|
70
|
+
ready({
|
|
71
|
+
where: i.banal.path,
|
|
71
72
|
fresh: i.fresh,
|
|
72
73
|
verified: "sha256 verified, and it measured a probe page",
|
|
73
|
-
})
|
|
74
|
+
});
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
77
|
* banal as the `ToolInstaller` port: download at the pin, check the sha256, accept by measuring a
|
|
@@ -88,12 +89,12 @@ export function banalInstaller(
|
|
|
88
89
|
...(source ? { source } : {}),
|
|
89
90
|
onDownload: report,
|
|
90
91
|
});
|
|
91
|
-
return r.ok ? ok(
|
|
92
|
+
return r.ok ? ok(readyOf(r.value)) : err(describe(r.error));
|
|
92
93
|
},
|
|
93
94
|
check() {
|
|
94
95
|
const r = checkBanal(d, s, source);
|
|
95
96
|
return r.ok
|
|
96
|
-
? ok(
|
|
97
|
+
? ok(readyOf({ banal: r.value, fresh: false }))
|
|
97
98
|
: err(describe(r.error));
|
|
98
99
|
},
|
|
99
100
|
};
|
|
@@ -15,14 +15,19 @@ export interface ShaMismatch {
|
|
|
15
15
|
readonly got: Sha256;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// The brand's one owner: the predicate IS the pin check, so no assertion is needed to mint the type.
|
|
19
|
+
const isPinned = (
|
|
20
|
+
bytes: Uint8Array,
|
|
21
|
+
source: BanalSource,
|
|
22
|
+
): bytes is PinnedBytes => sha256Hex(bytes) === source.sha256;
|
|
23
|
+
|
|
18
24
|
export function verifyPin(
|
|
19
25
|
bytes: Uint8Array,
|
|
20
26
|
source: BanalSource,
|
|
21
27
|
): Result<PinnedBytes, ShaMismatch> {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
: err({ expected: source.sha256, got });
|
|
28
|
+
return isPinned(bytes, source)
|
|
29
|
+
? ok(bytes)
|
|
30
|
+
: err({ expected: source.sha256, got: sha256Hex(bytes) });
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
/** What is installed, judged against the pin. */
|
|
@@ -11,15 +11,15 @@ import { tmpdir } from "node:os";
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { afterAll as after, test } from "vitest";
|
|
13
13
|
import { nodeAdapters } from "../node/index.ts";
|
|
14
|
-
import
|
|
14
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
15
|
+
import type { Scratch } from "../../ports/workspace.ts";
|
|
15
16
|
import {
|
|
16
17
|
banalCommand,
|
|
18
|
+
parseShQuoted,
|
|
17
19
|
shQuote,
|
|
18
20
|
stage,
|
|
19
21
|
stageBanalInput,
|
|
20
|
-
type StagedInput,
|
|
21
22
|
} from "./invocation.ts";
|
|
22
|
-
import type { LocatedBanal } from "./locate.ts";
|
|
23
23
|
import { XML_DIALECT } from "./xml.ts";
|
|
24
24
|
|
|
25
25
|
test("the staging holds BOTH files: the XML and the -v stub answering the dialect paperlint writes", () => {
|
|
@@ -32,11 +32,14 @@ test("the staging holds BOTH files: the XML and the -v stub answering the dialec
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
test("banalCommand: perl runs banal on the staged .xml, with $PDFTOHTML quoted", () => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
// A scratch that places the two files in different directories, one with a space.
|
|
36
|
+
const scratch: Scratch = {
|
|
37
|
+
dir: absolutePath("/s"),
|
|
38
|
+
write: (name) =>
|
|
39
|
+
absolutePath(name === "paper.xml" ? "/s/paper.xml" : "/s d/pdftohtml"),
|
|
40
|
+
};
|
|
41
|
+
const staged = stage(scratch, stageBanalInput([]));
|
|
42
|
+
const banal = { path: absolutePath("/b/banal") };
|
|
40
43
|
const c = banalCommand(banal, staged, { PATH: "/bin" });
|
|
41
44
|
assert.deepEqual(
|
|
42
45
|
[c.file, ...c.args],
|
|
@@ -50,8 +53,19 @@ test("shQuote survives a single quote", () => {
|
|
|
50
53
|
assert.equal(shQuote("it's"), `'it'"'"'s'`);
|
|
51
54
|
});
|
|
52
55
|
|
|
56
|
+
test("parseShQuoted refuses a word the shell would split or expand", () => {
|
|
57
|
+
assert.equal(parseShQuoted(`'a b'`), `'a b'`);
|
|
58
|
+
assert.throws(() => parseShQuoted("a b"), /not one single-quoted shell word/);
|
|
59
|
+
assert.throws(
|
|
60
|
+
() => parseShQuoted(`'a'b'`),
|
|
61
|
+
/not one single-quoted shell word/,
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
53
65
|
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-stage-test-")));
|
|
54
|
-
after(() =>
|
|
66
|
+
after(() => {
|
|
67
|
+
rmSync(root, { recursive: true, force: true });
|
|
68
|
+
});
|
|
55
69
|
|
|
56
70
|
test("🔴 stage: on a real scratch directory the stub answers `-v` through the shell, from a path with a space and a quote", () => {
|
|
57
71
|
const awkward = join(root, "it's a dir");
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* (`Error: Failed to run pdftohtml`, measured); told a version below 0.85 it applies a larger
|
|
8
8
|
* correction and every font size moves. So `$PDFTOHTML` (banal's own override, line 166) points at a
|
|
9
9
|
* stub that answers `-v` with `XML_DIALECT.version` — the pdftohtml whose XML `xml.ts` writes — and
|
|
10
|
-
* refuses anything else. `test/e2e/banal.
|
|
10
|
+
* refuses anything else. `test/e2e/banal.ts` shows both failures with the real banal.
|
|
11
11
|
*/
|
|
12
12
|
import type { Opaque } from "ts-essentials";
|
|
13
13
|
import type { AbsolutePath } from "../../domain/paths.ts";
|
|
14
14
|
import type { Command } from "../../ports/process.ts";
|
|
15
15
|
import type { Scratch } from "../../ports/workspace.ts";
|
|
16
|
-
import type {
|
|
16
|
+
import type { BanalCandidate } from "./locate.ts";
|
|
17
17
|
import type { PageLayout } from "../../domain/page-layout.ts";
|
|
18
18
|
import { pdf2xml, XML_DIALECT } from "./xml.ts";
|
|
19
19
|
|
|
@@ -23,9 +23,19 @@ export const BANAL_RUN_MS = 120_000;
|
|
|
23
23
|
/** A path quoted for `/bin/sh`. Minted by `shQuote` only. */
|
|
24
24
|
export type ShQuoted = Opaque<string, "ShQuoted">;
|
|
25
25
|
|
|
26
|
+
// One single-quoted word: any run of non-quotes, each quote spelled `'"'"'`. What `shQuote` writes.
|
|
27
|
+
const SH_QUOTED = /^'(?:[^']|'"'"')*'$/;
|
|
28
|
+
const isShQuoted = (s: string): s is ShQuoted => SH_QUOTED.test(s);
|
|
29
|
+
|
|
30
|
+
/** A word already quoted for `/bin/sh`, checked; anything else throws. */
|
|
31
|
+
export function parseShQuoted(s: string): ShQuoted {
|
|
32
|
+
if (!isShQuoted(s)) throw new Error(`not one single-quoted shell word: ${s}`);
|
|
33
|
+
return s;
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
/** banal interpolates `$PDFTOHTML` into a shell command unquoted — so the value is quoted here. */
|
|
27
37
|
export const shQuote = (s: string): ShQuoted =>
|
|
28
|
-
`'${s.replace(/'/g, `'"'"'`)}'`
|
|
38
|
+
parseShQuoted(`'${s.replace(/'/g, `'"'"'`)}'`);
|
|
29
39
|
|
|
30
40
|
/** The stub standing in for `pdftohtml`: it answers `-v` and refuses to convert anything. */
|
|
31
41
|
export const PDFTOHTML_STUB = [
|
|
@@ -64,11 +74,15 @@ export const stageBanalInput = (
|
|
|
64
74
|
],
|
|
65
75
|
});
|
|
66
76
|
|
|
77
|
+
// Not exported: only `stage` can write the property, so only it can make a StagedInput.
|
|
78
|
+
const STAGED = Symbol("StagedInput");
|
|
79
|
+
|
|
67
80
|
/** The staging written to disk. Minted by `stage` only: both files written, or nothing returned. */
|
|
68
|
-
export
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
export interface StagedInput {
|
|
82
|
+
readonly xml: AbsolutePath;
|
|
83
|
+
readonly stub: AbsolutePath;
|
|
84
|
+
readonly [STAGED]: true;
|
|
85
|
+
}
|
|
72
86
|
|
|
73
87
|
/** Write a staging into a scratch directory, the stub executable. The only minter of `StagedInput`. */
|
|
74
88
|
export function stage(s: Scratch, staging: BanalStaging): StagedInput {
|
|
@@ -76,12 +90,13 @@ export function stage(s: Scratch, staging: BanalStaging): StagedInput {
|
|
|
76
90
|
return {
|
|
77
91
|
xml: s.write(xml.name, xml.content, xml.mode),
|
|
78
92
|
stub: s.write(stub.name, stub.content, stub.mode),
|
|
79
|
-
|
|
93
|
+
[STAGED]: true,
|
|
94
|
+
};
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
/** `perl banal -no-time -json <xml>`, with `$PDFTOHTML` quoted by construction. */
|
|
83
98
|
export function banalCommand(
|
|
84
|
-
banal:
|
|
99
|
+
banal: Pick<BanalCandidate, "path">,
|
|
85
100
|
staged: StagedInput,
|
|
86
101
|
baseEnv: Readonly<Record<string, string>>,
|
|
87
102
|
): Command {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { test } from "vitest";
|
|
3
|
-
import
|
|
3
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
4
4
|
import { err } from "../../domain/result.ts";
|
|
5
5
|
import { installedBanal, lookupOrder, pickBanal } from "./locate.ts";
|
|
6
6
|
import { BANAL_PIN } from "./pin.ts";
|
|
7
7
|
import { parseBanalSettings } from "./settings.ts";
|
|
8
8
|
|
|
9
9
|
const dirs = { home: "/h", tmp: "/t", cwd: "/w" };
|
|
10
|
-
const project = "/p"
|
|
10
|
+
const project = absolutePath("/p");
|
|
11
11
|
const cache = `/h/.cache/paperlint/banal/${BANAL_PIN.commit.slice(0, 12)}/banal`;
|
|
12
12
|
const on =
|
|
13
13
|
(...files: string[]) =>
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
* `$BANAL` (an explicit choice — if it names nothing, there is NO fallback), `<project>/vendor/banal`
|
|
4
4
|
* (a project that vendors its own copy), then the one `paperlint toolchain` installed.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import type { Opaque } from "ts-essentials";
|
|
8
|
-
import type { AbsolutePath } from "../../domain/paths.ts";
|
|
6
|
+
import { joinPath, type AbsolutePath } from "../../domain/paths.ts";
|
|
9
7
|
import { err, ok, type Result } from "../../domain/result.ts";
|
|
10
8
|
import { BANAL_PIN } from "./pin.ts";
|
|
11
9
|
import type { BanalSettings } from "./settings.ts";
|
|
@@ -20,14 +18,20 @@ export interface BanalCandidate {
|
|
|
20
18
|
readonly provenance: Provenance;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
// Not exported: only this module can write the property, so only `locate` can make a LocatedBanal.
|
|
22
|
+
const LOCATED = Symbol("LocatedBanal");
|
|
25
23
|
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*/
|
|
30
|
-
|
|
24
|
+
/** A candidate the `Files` port confirmed is a file. Minted by `pickBanal` (through `locate`) only. */
|
|
25
|
+
export type LocatedBanal = BanalCandidate & { readonly [LOCATED]: true };
|
|
26
|
+
|
|
27
|
+
/** `path` as a banal to run — when the `Files` port says it is a file, else null. */
|
|
28
|
+
function locate(
|
|
29
|
+
path: AbsolutePath,
|
|
30
|
+
kind: Provenance["kind"],
|
|
31
|
+
isFile: (p: AbsolutePath) => boolean,
|
|
32
|
+
): LocatedBanal | null {
|
|
33
|
+
return isFile(path) ? { path, provenance: { kind }, [LOCATED]: true } : null;
|
|
34
|
+
}
|
|
31
35
|
|
|
32
36
|
/** Why there is no banal to run. */
|
|
33
37
|
export type BanalMissing =
|
|
@@ -47,7 +51,7 @@ export type LookupOrder =
|
|
|
47
51
|
|
|
48
52
|
/** Where `paperlint toolchain` puts the pinned banal: one directory per HotCRP commit. */
|
|
49
53
|
export const installedBanal = (s: BanalSettings): AbsolutePath =>
|
|
50
|
-
|
|
54
|
+
joinPath(s.cacheDir, BANAL_PIN.commit.slice(0, 12), "banal");
|
|
51
55
|
|
|
52
56
|
export function lookupOrder(
|
|
53
57
|
s: BanalSettings,
|
|
@@ -56,25 +60,27 @@ export function lookupOrder(
|
|
|
56
60
|
if (s.explicit) return { kind: "explicit", path: s.explicit };
|
|
57
61
|
return {
|
|
58
62
|
kind: "search",
|
|
59
|
-
vendor:
|
|
63
|
+
vendor: joinPath(projectRoot, "vendor", "banal"),
|
|
60
64
|
cache: installedBanal(s),
|
|
61
65
|
};
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
const located = (path: AbsolutePath, kind: Provenance["kind"]) =>
|
|
65
|
-
ok({ path, provenance: { kind } } as LocatedBanal);
|
|
66
|
-
|
|
67
68
|
export function pickBanal(
|
|
68
69
|
order: LookupOrder,
|
|
69
70
|
isFile: (p: AbsolutePath) => boolean,
|
|
70
71
|
): Result<LocatedBanal, BanalMissing> {
|
|
71
|
-
if (order.kind === "explicit")
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
if (order.kind === "explicit") {
|
|
73
|
+
const own = locate(order.path, "env", isFile);
|
|
74
|
+
return own
|
|
75
|
+
? ok(own)
|
|
74
76
|
: err({ kind: "explicit-not-found", path: order.path });
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
}
|
|
78
|
+
const found =
|
|
79
|
+
locate(order.vendor, "vendor", isFile) ??
|
|
80
|
+
locate(order.cache, "cache", isFile);
|
|
81
|
+
return found
|
|
82
|
+
? ok(found)
|
|
83
|
+
: err({ kind: "not-installed", installed: order.cache });
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
/** Which rule found it, as a person reads it. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `output.ts` — every way a banal run can end, as a table of `ProcessExit` → `Result`, and the
|
|
3
|
-
* measurement → geometry projection. Pure: no perl, no disk. The real banal is `test/e2e/banal.
|
|
3
|
+
* measurement → geometry projection. Pure: no perl, no disk. The real banal is `test/e2e/banal.ts`.
|
|
4
4
|
*/
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
6
|
import { test } from "vitest";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `adapters/banal/run.ts` — running and installing banal, on in-memory ports: which `Command` it builds, what it stages,
|
|
3
3
|
* what it downloads and writes, and what it concludes. No perl, no disk, no network: the adapters
|
|
4
|
-
* record, and each assertion reads the record. Real perl and the real banal are `test/e2e/banal.
|
|
4
|
+
* record, and each assertion reads the record. Real perl and the real banal are `test/e2e/banal.ts`;
|
|
5
5
|
* the adapters' own behaviour is `src/adapters/node/*.test.ts`.
|
|
6
6
|
*/
|
|
7
7
|
import assert from "node:assert/strict";
|
|
@@ -18,7 +18,7 @@ import { checkBanal, ensureBanal, measureGeometry } from "./run.ts";
|
|
|
18
18
|
import { sha256Hex } from "../../domain/sha256.ts";
|
|
19
19
|
import { installedBanal } from "./locate.ts";
|
|
20
20
|
import { parseBanalSettings } from "./settings.ts";
|
|
21
|
-
import
|
|
21
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
22
22
|
import type { Command, ProcessExit } from "../../ports/process.ts";
|
|
23
23
|
|
|
24
24
|
const s = parseBanalSettings(
|
|
@@ -29,7 +29,7 @@ const noExplicit = parseBanalSettings(
|
|
|
29
29
|
{ PATH: "/bin" },
|
|
30
30
|
{ home: "/h", tmp: "/t", cwd: "/w" },
|
|
31
31
|
);
|
|
32
|
-
const project = "/p"
|
|
32
|
+
const project = absolutePath("/p");
|
|
33
33
|
/** What banal prints for the one-page probe. */
|
|
34
34
|
const MEASURED = '{"bodyfontsize": 10.3, "columns": 1, "pages": [{}]}';
|
|
35
35
|
const banalAnswers = (e: ProcessExit) =>
|
|
@@ -45,10 +45,10 @@ test("measureGeometry: perl runs the found banal on the staged XML, and the geom
|
|
|
45
45
|
});
|
|
46
46
|
const g = measureGeometry(io, s, project, []);
|
|
47
47
|
assert.equal(g.source, "banal");
|
|
48
|
-
assert.equal(g.
|
|
48
|
+
assert.equal(g.geometry.bodyPt, 10.3);
|
|
49
49
|
const [c] = run.calls;
|
|
50
50
|
assert.equal(c?.file, "perl");
|
|
51
|
-
assert.equal(c
|
|
51
|
+
assert.equal(c.args.at(-1), "/scratch/paper.xml");
|
|
52
52
|
// Guards: both files staged, once, and the scratch scope ended normally (cleanup ran).
|
|
53
53
|
assert.deepEqual(
|
|
54
54
|
workspace.written.map((w) => [w.name, w.mode]),
|
|
@@ -44,8 +44,6 @@ import {
|
|
|
44
44
|
lookupOrder,
|
|
45
45
|
pickBanal,
|
|
46
46
|
type BanalCandidate,
|
|
47
|
-
type LocatedBanal,
|
|
48
|
-
type PinnedBanal,
|
|
49
47
|
} from "./locate.ts";
|
|
50
48
|
import {
|
|
51
49
|
geometryOf,
|
|
@@ -89,11 +87,14 @@ export type BanalOutcome =
|
|
|
89
87
|
readonly tried: BanalCandidate | null;
|
|
90
88
|
};
|
|
91
89
|
|
|
92
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Run `banal` on `pages` in a scratch directory that is gone when this returns. It needs the path
|
|
92
|
+
* only: `measureGeometry` passes the banal `pickBanal` located, `probe` the one whose bytes it verified.
|
|
93
|
+
*/
|
|
93
94
|
function runBanal(
|
|
94
95
|
d: BanalDeps,
|
|
95
96
|
s: BanalSettings,
|
|
96
|
-
banal:
|
|
97
|
+
banal: Pick<BanalCandidate, "path">,
|
|
97
98
|
pages: readonly PageLayout[],
|
|
98
99
|
): Result<BanalMeasurement, BanalFailure> {
|
|
99
100
|
return d.workspace.within("paperlint-banal-", (scratch) =>
|
|
@@ -148,6 +149,15 @@ function perlRuns(d: BanalDeps, s: BanalSettings): boolean {
|
|
|
148
149
|
return r.kind === "exited" && r.status === 0;
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
// Not exported: only `probe` below can write the property, so only it can make a PinnedBanal.
|
|
153
|
+
const PINNED = Symbol("PinnedBanal");
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* `paperlint toolchain`'s banal after its bytes hashed to the pin AND it measured the probe page. Minted by
|
|
157
|
+
* `probe` only, for `ensureBanal` / `checkBanal` (`./index.ts`): a "ready" line cannot be printed without one.
|
|
158
|
+
*/
|
|
159
|
+
export type PinnedBanal = BanalCandidate & { readonly [PINNED]: true };
|
|
160
|
+
|
|
151
161
|
/**
|
|
152
162
|
* Does the installed, pin-verified banal RUN — accepted by measuring the probe page, not by a
|
|
153
163
|
* download's exit code. The one place a `PinnedBanal` is minted; callers verified the bytes first.
|
|
@@ -157,13 +167,9 @@ function probe(
|
|
|
157
167
|
s: BanalSettings,
|
|
158
168
|
path: AbsolutePath,
|
|
159
169
|
): Result<PinnedBanal, BanalFailure> {
|
|
160
|
-
const
|
|
161
|
-
const r = andThen(
|
|
162
|
-
runBanal(d, s, at as LocatedBanal, [PROBE_PAGE]),
|
|
163
|
-
acceptProbe,
|
|
164
|
-
);
|
|
170
|
+
const r = andThen(runBanal(d, s, { path }, [PROBE_PAGE]), acceptProbe);
|
|
165
171
|
return r.ok
|
|
166
|
-
? ok(
|
|
172
|
+
? ok({ path, provenance: { kind: "cache" }, [PINNED]: true })
|
|
167
173
|
: err({ kind: "does-not-run", path, why: r.error });
|
|
168
174
|
}
|
|
169
175
|
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type Environment,
|
|
10
10
|
type HostDirs,
|
|
11
11
|
} from "../../domain/host.ts";
|
|
12
|
-
import type
|
|
12
|
+
import { absolutePath, type AbsolutePath } from "../../domain/paths.ts";
|
|
13
13
|
|
|
14
14
|
/** An explicit banal to use instead of paperlint's (a path to the script). */
|
|
15
15
|
export const BANAL_ENV = "BANAL";
|
|
@@ -29,7 +29,7 @@ export interface BanalSettings {
|
|
|
29
29
|
|
|
30
30
|
/** `resolve` with an absolute base never consults the process's cwd. */
|
|
31
31
|
const absolute = (cwd: string, p: string): AbsolutePath =>
|
|
32
|
-
resolve(cwd, p)
|
|
32
|
+
absolutePath(resolve(cwd, p));
|
|
33
33
|
|
|
34
34
|
/** An unset variable and an empty one mean the same: not set. */
|
|
35
35
|
const set = (v: string | undefined): string | null => (v ? v : null);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Every page here is written by hand. The real-PDF half — pdf.js boxes of a committed fixture,
|
|
6
6
|
* written as XML, measured by the real banal and compared with banal on real pdftohtml — is
|
|
7
|
-
* `test/e2e/banal.
|
|
7
|
+
* `test/e2e/banal.ts`, which runs where `paperlint toolchain` has installed banal.
|
|
8
8
|
*/
|
|
9
9
|
import assert from "node:assert/strict";
|
|
10
10
|
import { test } from "vitest";
|
|
@@ -15,16 +15,18 @@ import { tmpdir } from "node:os";
|
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { pathToFileURL } from "node:url";
|
|
17
17
|
import { afterAll as after, test } from "vitest";
|
|
18
|
-
import
|
|
18
|
+
import { absolutePath, joinPath } from "../../domain/paths.ts";
|
|
19
19
|
import { curlDownload, curlFailure } from "./download.io.ts";
|
|
20
20
|
import type { ProcessExit } from "../../ports/process.ts";
|
|
21
21
|
import { spawnProcess } from "../node/index.ts";
|
|
22
22
|
|
|
23
|
-
const root =
|
|
24
|
-
mkdtempSync(join(tmpdir(), "paperlint-download-test-")),
|
|
23
|
+
const root = absolutePath(
|
|
24
|
+
realpathSync(mkdtempSync(join(tmpdir(), "paperlint-download-test-"))),
|
|
25
25
|
);
|
|
26
|
-
after(() =>
|
|
27
|
-
|
|
26
|
+
after(() => {
|
|
27
|
+
rmSync(root, { recursive: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
const at = (...p: string[]) => joinPath(root, ...p);
|
|
28
30
|
|
|
29
31
|
const dl = (env: Record<string, string>) => {
|
|
30
32
|
const tmp = at("dl-tmp");
|
|
@@ -45,7 +47,7 @@ test("Download: a URL that does not answer is a failure with curl's words", () =
|
|
|
45
47
|
const { d } = dl({ PATH: "/usr/bin:/bin" });
|
|
46
48
|
const r = d.fetch(pathToFileURL(at("missing")).href, 10_000);
|
|
47
49
|
assert.equal(r.ok, false);
|
|
48
|
-
assert.ok(
|
|
50
|
+
assert.ok(r.error.detail.length > 0);
|
|
49
51
|
});
|
|
50
52
|
|
|
51
53
|
test("🔴 Download: no curl on PATH is named as that, not as a failed URL", () => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* In-memory adapters for the app layer's tests: every port, no disk, no processes, no network.
|
|
3
3
|
* Each one RECORDS what it was asked, so a test asserts on the calls instead of on side effects.
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
6
6
|
import type { Download } from "../../ports/download.ts";
|
|
7
7
|
import type { Files } from "../../ports/files.ts";
|
|
8
8
|
import type { Command, ProcessExit, RunProcess } from "../../ports/process.ts";
|
|
@@ -69,11 +69,12 @@ export interface MemoryWorkspace extends Workspace {
|
|
|
69
69
|
export function memoryWorkspace(dir = "/scratch"): MemoryWorkspace {
|
|
70
70
|
const written: MemoryWorkspace["written"] = [];
|
|
71
71
|
const ended: ("returned" | "threw")[] = [];
|
|
72
|
+
const at = absolutePath(dir);
|
|
72
73
|
const scratch: Scratch = {
|
|
73
|
-
dir:
|
|
74
|
+
dir: at,
|
|
74
75
|
write(name, content, mode) {
|
|
75
76
|
written.push({ name, content, mode });
|
|
76
|
-
return `${dir}/${name}`
|
|
77
|
+
return absolutePath(`${dir}/${name}`);
|
|
77
78
|
},
|
|
78
79
|
};
|
|
79
80
|
return {
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
import { dirname } from "node:path";
|
|
11
11
|
import type { AbsolutePath } from "../../domain/paths.ts";
|
|
12
12
|
import type { Files } from "../../ports/files.ts";
|
|
13
|
+
import { codeOf } from "../../domain/text.ts";
|
|
13
14
|
|
|
14
15
|
/** An error's errno code; anything thrown that is not an Error has none. */
|
|
15
|
-
export const errorCode = (e: unknown): unknown =>
|
|
16
|
-
e instanceof Error ? (e as NodeJS.ErrnoException).code : undefined;
|
|
16
|
+
export const errorCode = (e: unknown): unknown => codeOf(e);
|
|
17
17
|
|
|
18
18
|
export const nodeFiles: Files = {
|
|
19
19
|
isFile(p: AbsolutePath): boolean {
|
|
@@ -4,12 +4,16 @@ import { existsSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { afterAll as after, test } from "vitest";
|
|
7
|
-
import
|
|
7
|
+
import { absolutePath, joinPath } from "../../domain/paths.ts";
|
|
8
8
|
import { errorCode, nodeFiles } from "./files.io.ts";
|
|
9
9
|
|
|
10
|
-
const root =
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const root = absolutePath(
|
|
11
|
+
realpathSync(mkdtempSync(join(tmpdir(), "paperlint-files-test-"))),
|
|
12
|
+
);
|
|
13
|
+
after(() => {
|
|
14
|
+
rmSync(root, { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
const at = (...p: string[]) => joinPath(root, ...p);
|
|
13
17
|
|
|
14
18
|
test("Files: a missing file reads as null and is not a file", () => {
|
|
15
19
|
assert.equal(nodeFiles.readBytes(at("nope")), null);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* it — `error`, `status`, `signal`, all nullable — and turns it into a `ProcessExit`, so no caller
|
|
4
4
|
* re-derives "what happened" from three fields.
|
|
5
5
|
*/
|
|
6
|
+
import { codeOf, printed } from "../../domain/text.ts";
|
|
6
7
|
import {
|
|
7
8
|
spawnSync,
|
|
8
9
|
type SpawnSyncOptionsWithStringEncoding,
|
|
@@ -10,17 +11,24 @@ import {
|
|
|
10
11
|
} from "node:child_process";
|
|
11
12
|
import type { Command, ProcessExit, RunProcess } from "../../ports/process.ts";
|
|
12
13
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Node's `spawnSync` — the one overload this file calls, text in and text out — or a stand-in with its
|
|
16
|
+
* shape (a harness records the calls through one).
|
|
17
|
+
*/
|
|
18
|
+
export type SpawnSync = (
|
|
19
|
+
file: string,
|
|
20
|
+
args: readonly string[],
|
|
21
|
+
options: SpawnSyncOptionsWithStringEncoding,
|
|
22
|
+
) => SpawnSyncReturns<string>;
|
|
15
23
|
|
|
16
24
|
const errno = (r: SpawnSyncReturns<string>): string | undefined =>
|
|
17
|
-
(r.error
|
|
25
|
+
codeOf(r.error);
|
|
18
26
|
|
|
19
27
|
/** What a finished `spawnSync` came to. ENOENT is the executable itself: `c.file` was not found. */
|
|
20
28
|
export function exitOf(r: SpawnSyncReturns<string>, c: Command): ProcessExit {
|
|
21
29
|
const out = {
|
|
22
|
-
stdout:
|
|
23
|
-
stderr:
|
|
30
|
+
stdout: printed(r.stdout),
|
|
31
|
+
stderr: printed(r.stderr),
|
|
24
32
|
};
|
|
25
33
|
const code = errno(r);
|
|
26
34
|
if (code === "ENOENT") return { kind: "not-found", file: c.file };
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import assert from "node:assert/strict";
|
|
7
7
|
import { test } from "vitest";
|
|
8
|
-
import
|
|
8
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
9
9
|
import type { Command } from "../../ports/process.ts";
|
|
10
10
|
import { spawnProcess } from "./process.io.ts";
|
|
11
11
|
|
|
@@ -58,23 +58,23 @@ test("a program killed by a signal is `signalled`, with what it printed first",
|
|
|
58
58
|
test("output past maxOutputBytes is a spawn failure, named by Node's own message", () => {
|
|
59
59
|
const r = run.run({ ...sh("head -c 100000 /dev/zero"), maxOutputBytes: 10 });
|
|
60
60
|
assert.equal(r.kind, "spawn-failed");
|
|
61
|
-
assert.match(r.
|
|
61
|
+
assert.match(r.message, /ENOBUFS/);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
test("cwd is the Command's when it names one", () => {
|
|
65
|
-
const r = run.run({ ...sh("pwd"), cwd: "/"
|
|
65
|
+
const r = run.run({ ...sh("pwd"), cwd: absolutePath("/") });
|
|
66
66
|
assert.deepEqual(r.kind === "exited" && r.stdout, "/\n");
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
test("a spawn that reports no status, no signal and no error reads as exit 1, not success", () => {
|
|
70
|
-
const silent = spawnProcess((
|
|
70
|
+
const silent = spawnProcess(() => ({
|
|
71
71
|
pid: 0,
|
|
72
72
|
output: [],
|
|
73
73
|
stdout: "",
|
|
74
74
|
stderr: "",
|
|
75
75
|
status: null,
|
|
76
76
|
signal: null,
|
|
77
|
-
}))
|
|
77
|
+
}));
|
|
78
78
|
assert.deepEqual(silent.run(sh("true")), {
|
|
79
79
|
kind: "exited",
|
|
80
80
|
status: 1,
|
|
@@ -10,14 +10,18 @@ import {
|
|
|
10
10
|
writeFileSync,
|
|
11
11
|
} from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
-
import
|
|
13
|
+
import {
|
|
14
|
+
absolutePath,
|
|
15
|
+
joinPath,
|
|
16
|
+
type AbsolutePath,
|
|
17
|
+
} from "../../domain/paths.ts";
|
|
14
18
|
import type { NotAPromise, Scratch, Workspace } from "../../ports/workspace.ts";
|
|
15
19
|
|
|
16
20
|
function scratchIn(dir: AbsolutePath): Scratch {
|
|
17
21
|
return {
|
|
18
22
|
dir,
|
|
19
23
|
write(name, content, mode): AbsolutePath {
|
|
20
|
-
const at =
|
|
24
|
+
const at = joinPath(dir, name);
|
|
21
25
|
writeFileSync(at, content);
|
|
22
26
|
if (mode === "exec") chmodSync(at, 0o755);
|
|
23
27
|
return at;
|
|
@@ -29,9 +33,10 @@ function scratchIn(dir: AbsolutePath): Scratch {
|
|
|
29
33
|
export function tmpWorkspace(tmpDir: string): Workspace {
|
|
30
34
|
return {
|
|
31
35
|
within<T>(prefix: string, use: (s: Scratch) => NotAPromise<T>): T {
|
|
32
|
-
|
|
36
|
+
// `realpath` answers with an absolute path, whatever `tmpDir` was.
|
|
37
|
+
const dir = absolutePath(realpathSync(mkdtempSync(join(tmpDir, prefix))));
|
|
33
38
|
try {
|
|
34
|
-
return use(scratchIn(dir
|
|
39
|
+
return use(scratchIn(dir));
|
|
35
40
|
} finally {
|
|
36
41
|
rmSync(dir, { recursive: true, force: true });
|
|
37
42
|
}
|