paperlint 7.0.0 → 7.2.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 +3 -0
- package/.github/workflows/deadlines.yml +100 -0
- package/README.md +24 -11
- package/dist/adapters/hotcrp/deadlines.d.ts +8 -0
- package/dist/adapters/hotcrp/deadlines.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts +6 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.js +26 -0
- package/dist/adapters/hotcrp/deadlines.io.js.map +1 -0
- package/dist/adapters/hotcrp/deadlines.js +134 -0
- package/dist/adapters/hotcrp/deadlines.js.map +1 -0
- package/dist/adapters/hotcrp/index.d.ts +2 -0
- package/dist/adapters/hotcrp/index.d.ts.map +1 -1
- package/dist/adapters/hotcrp/index.js +2 -0
- package/dist/adapters/hotcrp/index.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +42 -7
- package/dist/cli.js.map +1 -1
- package/dist/cycle-rules.d.ts +24 -0
- package/dist/cycle-rules.d.ts.map +1 -0
- package/dist/cycle-rules.js +212 -0
- package/dist/cycle-rules.js.map +1 -0
- package/dist/domain/body-pages.d.ts +29 -0
- package/dist/domain/body-pages.d.ts.map +1 -1
- package/dist/domain/body-pages.js +25 -0
- package/dist/domain/body-pages.js.map +1 -1
- package/dist/domain/cycle.d.ts +148 -0
- package/dist/domain/cycle.d.ts.map +1 -0
- package/dist/domain/cycle.js +281 -0
- package/dist/domain/cycle.js.map +1 -0
- package/dist/domain/deadline.d.ts +128 -0
- package/dist/domain/deadline.d.ts.map +1 -0
- package/dist/domain/deadline.js +281 -0
- package/dist/domain/deadline.js.map +1 -0
- package/dist/eslint-rules/cycle-stage.d.ts +39 -0
- package/dist/eslint-rules/cycle-stage.d.ts.map +1 -0
- package/dist/eslint-rules/cycle-stage.js +119 -0
- package/dist/eslint-rules/cycle-stage.js.map +1 -0
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +2 -0
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/paper-includes.d.ts +3 -0
- package/dist/paper-includes.d.ts.map +1 -1
- package/dist/paper-includes.js +17 -2
- package/dist/paper-includes.js.map +1 -1
- package/dist/paper-settings.d.ts +7 -0
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +61 -8
- package/dist/paper-settings.js.map +1 -1
- package/dist/ports/venue-deadlines.d.ts +14 -0
- package/dist/ports/venue-deadlines.d.ts.map +1 -0
- package/dist/ports/venue-deadlines.js +2 -0
- package/dist/ports/venue-deadlines.js.map +1 -0
- package/dist/presets.d.ts +22 -3
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +26 -17
- package/dist/presets.js.map +1 -1
- package/dist/submission.d.ts +16 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +59 -16
- package/dist/submission.js.map +1 -1
- package/dist/tex-requirements.d.ts +12 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +26 -3
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +24 -2
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js +98 -10
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/venue-rules.d.ts +12 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +81 -17
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +140 -12
- package/docs/e2e.md +14 -12
- package/docs/rules/cycle/evidence.md +87 -0
- package/docs/rules/cycle/port-done.md +75 -0
- package/docs/rules/cycle/record.md +152 -0
- package/docs/rules/cycle/stage.md +85 -0
- package/docs/rules/format/page-limit.md +18 -1
- package/docs/rules/tex/template.md +25 -5
- package/docs/rules/tex/venue-leftover.md +13 -0
- package/docs/rules.md +68 -55
- package/docs/submission.md +10 -1
- package/eslint-rules/cycle-stage.test.mjs +131 -0
- package/eslint-rules/cycle-stage.ts +141 -0
- package/eslint.config.mjs +41 -1
- package/fixtures/build-e2e/msr/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/msr/expect.json +18 -0
- package/fixtures/build-e2e/msr/paper.tex +49 -0
- package/fixtures/build-e2e/msr/paperlint.json +1 -0
- package/fixtures/build-e2e/msr-appendix-after/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/msr-appendix-after/expect.json +20 -0
- package/fixtures/build-e2e/msr-appendix-after/paper.tex +48 -0
- package/fixtures/build-e2e/msr-appendix-after/paperlint.json +1 -0
- package/lib/paper-config.ts +3 -0
- package/package.json +1 -1
- package/presets/aidc.jsonc +24 -3
- package/presets/aidc.md +6 -3
- package/presets/ieee-conference.md +1 -1
- package/presets/msr.jsonc +95 -0
- package/presets/msr.md +98 -0
- package/presets/venue-profile.schema.json +64 -0
- package/scripts/check.ts +4 -0
- package/scripts/refresh-deadlines.test.ts +431 -0
- package/scripts/refresh-deadlines.ts +387 -0
- package/skills/camera-ready/SKILL.md +8 -1
- package/skills/camera-ready/SKILL.md.spec.ts +7 -0
- package/skills/submission-portal/SKILL.md +2 -2
- package/skills/submission-portal/SKILL.md.spec.ts +1 -1
- package/skills/submit-paper/SKILL.md +9 -1
- package/skills/submit-paper/SKILL.md.spec.ts +8 -0
- package/src/adapters/hotcrp/cassette/README.md +22 -0
- package/src/adapters/hotcrp/cassette/deadlines-aidc26.html +17 -0
- package/src/adapters/hotcrp/cassette/deadlines-fse2027.html +24 -0
- package/src/adapters/hotcrp/cassette/deadlines-icse2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-msr2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-nosuch.html +18 -0
- package/src/adapters/hotcrp/cassette/deadlines-sosp26.html +26 -0
- package/src/adapters/hotcrp/deadlines.io.test.ts +86 -0
- package/src/adapters/hotcrp/deadlines.io.ts +27 -0
- package/src/adapters/hotcrp/deadlines.test.ts +170 -0
- package/src/adapters/hotcrp/deadlines.ts +161 -0
- package/src/adapters/hotcrp/index.ts +2 -0
- package/src/cli.test.ts +9 -0
- package/src/cli.ts +50 -6
- package/src/cycle-rules.test.ts +278 -0
- package/src/cycle-rules.ts +273 -0
- package/src/domain/body-pages.test.ts +144 -0
- package/src/domain/body-pages.ts +61 -1
- package/src/domain/cycle.test.ts +456 -0
- package/src/domain/cycle.ts +432 -0
- package/src/domain/deadline.test.ts +386 -0
- package/src/domain/deadline.ts +472 -0
- package/src/new-paper.test.ts +28 -0
- package/src/paper-includes.test.ts +26 -0
- package/src/paper-includes.ts +20 -2
- package/src/paper-settings-commands.test.ts +1 -1
- package/src/paper-settings.test.ts +167 -0
- package/src/paper-settings.ts +85 -11
- package/src/ports/venue-deadlines.ts +14 -0
- package/src/preset-card-rule.test.ts +1 -0
- package/src/presets.test.ts +171 -1
- package/src/presets.ts +65 -15
- package/src/rule-cases.test.ts +144 -0
- package/src/submission.test.ts +147 -4
- package/src/submission.ts +100 -18
- package/src/tex-requirements.ts +52 -4
- package/src/tex-venue-rules.test.ts +237 -0
- package/src/tex-venue-rules.ts +130 -15
- package/src/venue-rules.test.ts +255 -2
- package/src/venue-rules.ts +115 -22
package/.github/workflows/ci.yml
CHANGED
|
@@ -44,6 +44,9 @@ on:
|
|
|
44
44
|
# with zero checks. macOS is a cell of the build-e2e matrix below.
|
|
45
45
|
pull_request:
|
|
46
46
|
types: [opened, synchronize, reopened, ready_for_review]
|
|
47
|
+
# The deadlines bot's pull request (deadlines.yml) is opened with GITHUB_TOKEN, which starts no
|
|
48
|
+
# other workflow; that job dispatches this one on its branch, so the PR's head commit gets checks.
|
|
49
|
+
workflow_dispatch:
|
|
47
50
|
|
|
48
51
|
# A newer push supersedes an older one on the same ref. Note this does NOT make the cancelled
|
|
49
52
|
# run free — cancellation arrives after the expensive steps have already run and been billed —
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# THE VENUE PRESETS' DEADLINES, RE-READ FROM THEIR PORTALS ONCE A DAY.
|
|
2
|
+
#
|
|
3
|
+
# A shipped venue preset (presets/msr.jsonc, presets/aidc.jsonc) carries its portal's deadlines,
|
|
4
|
+
# each with the day it was read. Chairs move deadlines; a paper only learns it if the preset does.
|
|
5
|
+
# So this job reads every preset's portal (`scripts/refresh-deadlines.ts`, through the HotCRP adapter
|
|
6
|
+
# in src/adapters/hotcrp/ — the public deadlines page, no account, no secret) and, when a value
|
|
7
|
+
# changed, opens ONE pull request with the new values, or updates the one already open. Its commit
|
|
8
|
+
# and title are `fix(presets): …`, which semantic-release (.releaserc.json, conventionalcommits)
|
|
9
|
+
# releases as a PATCH; a paper's normal dependency update (Dependabot) then brings the new dates.
|
|
10
|
+
# Nothing in a paper's own lint reads the network or the clock.
|
|
11
|
+
#
|
|
12
|
+
# 🔴 IT REPORTS, IT DOES NOT BLOCK. A portal that is down or gone is a `::warning::` annotation and a
|
|
13
|
+
# line in the pull request's body, and the script exits 0: the venue's outage is not a defect of this
|
|
14
|
+
# repository, and tomorrow's run reads it again. Only a preset that does not parse fails the run
|
|
15
|
+
# (exit 1) — that one IS ours, and `ci` fails on it too. `ci` itself never touches the network for
|
|
16
|
+
# this: scripts/refresh-deadlines.test.ts replays the pages recorded in src/adapters/hotcrp/cassette/.
|
|
17
|
+
#
|
|
18
|
+
# ⚠️ A pull request opened with GITHUB_TOKEN does not start other workflows (GitHub's rule against
|
|
19
|
+
# loops), so `ci` would never run on it and the PR could not show green. The last step therefore
|
|
20
|
+
# dispatches `ci` on the branch (ci.yml declares `workflow_dispatch` for this); its checks land on
|
|
21
|
+
# the branch's head commit, which is what the pull request shows. Two repository settings are
|
|
22
|
+
# needed, and both are switches in Settings → Actions → General, not in this file: workflows may
|
|
23
|
+
# have "Read and write permissions", and "Allow GitHub Actions to create and approve pull requests".
|
|
24
|
+
#
|
|
25
|
+
# ⛽ The repository is public, so the minutes are free (CLAUDE.md, "Cost"). Measured locally
|
|
26
|
+
# 2026-10-05: the script reads both portals in about 2 s; `npm ci` dominates.
|
|
27
|
+
name: deadlines
|
|
28
|
+
|
|
29
|
+
on:
|
|
30
|
+
schedule:
|
|
31
|
+
# 05:17 UTC daily: off the top of the hour, when GitHub's scheduler is busiest and runs late.
|
|
32
|
+
- cron: "17 5 * * *"
|
|
33
|
+
workflow_dispatch:
|
|
34
|
+
|
|
35
|
+
# One run at a time: two runs pushing the same branch would race.
|
|
36
|
+
concurrency:
|
|
37
|
+
group: deadlines
|
|
38
|
+
cancel-in-progress: false
|
|
39
|
+
|
|
40
|
+
# Push the bot's branch · open or edit its pull request · dispatch `ci` on it. Nothing else.
|
|
41
|
+
permissions:
|
|
42
|
+
contents: write
|
|
43
|
+
pull-requests: write
|
|
44
|
+
actions: write
|
|
45
|
+
|
|
46
|
+
jobs:
|
|
47
|
+
refresh:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
timeout-minutes: 10
|
|
50
|
+
env:
|
|
51
|
+
BRANCH: bot/preset-deadlines
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v7
|
|
54
|
+
|
|
55
|
+
- uses: actions/setup-node@v7
|
|
56
|
+
with:
|
|
57
|
+
node-version: "22"
|
|
58
|
+
cache: npm
|
|
59
|
+
|
|
60
|
+
# `--ignore-scripts`: the script runs from source (Node strips the types); `prepare`'s tsc
|
|
61
|
+
# build is not needed to read a page and rewrite a JSONC file.
|
|
62
|
+
- name: npm ci
|
|
63
|
+
run: npm ci --ignore-scripts
|
|
64
|
+
timeout-minutes: 5
|
|
65
|
+
|
|
66
|
+
- name: read every preset's portal
|
|
67
|
+
run: node scripts/refresh-deadlines.ts --summary "$RUNNER_TEMP/deadlines.md"
|
|
68
|
+
timeout-minutes: 3
|
|
69
|
+
|
|
70
|
+
- name: open or update the pull request
|
|
71
|
+
env:
|
|
72
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
73
|
+
REPO: ${{ github.repository }}
|
|
74
|
+
run: |
|
|
75
|
+
set -euo pipefail
|
|
76
|
+
if git diff --quiet -- presets; then
|
|
77
|
+
echo "no deadline changed — nothing to propose"
|
|
78
|
+
exit 0
|
|
79
|
+
fi
|
|
80
|
+
git config user.name "github-actions[bot]"
|
|
81
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
82
|
+
git switch -C "$BRANCH"
|
|
83
|
+
# Only the presets: the script writes nothing else, and nothing else may ride along.
|
|
84
|
+
git add -- presets
|
|
85
|
+
git commit -F - <<'MSG'
|
|
86
|
+
fix(presets): venue deadlines as their portals publish them now
|
|
87
|
+
|
|
88
|
+
Re-read by .github/workflows/deadlines.yml (scripts/refresh-deadlines.ts); the pull
|
|
89
|
+
request's body lists each value that changed and where it was read.
|
|
90
|
+
MSG
|
|
91
|
+
# The branch is the bot's own and is rebuilt from main on every run: force is the point.
|
|
92
|
+
git push --force origin "$BRANCH"
|
|
93
|
+
title="fix(presets): venue deadlines as their portals publish them now"
|
|
94
|
+
open=$(gh pr list --repo "$REPO" --head "$BRANCH" --state open --json number --jq '.[0].number // empty')
|
|
95
|
+
if [ -z "$open" ]; then
|
|
96
|
+
gh pr create --repo "$REPO" --base main --head "$BRANCH" --title "$title" --body-file "$RUNNER_TEMP/deadlines.md"
|
|
97
|
+
else
|
|
98
|
+
gh pr edit "$open" --repo "$REPO" --title "$title" --body-file "$RUNNER_TEMP/deadlines.md"
|
|
99
|
+
fi
|
|
100
|
+
gh workflow run ci.yml --repo "$REPO" --ref "$BRANCH"
|
package/README.md
CHANGED
|
@@ -30,6 +30,11 @@ before you submit.** It comes in two parts:
|
|
|
30
30
|
- 🧠 **Skills for [Claude Code](https://claude.com/claude-code)** — optional, covering the
|
|
31
31
|
whole pipeline: the idea, the venue, the study, the draft, the reviews, submission.
|
|
32
32
|
|
|
33
|
+
> ⚠️ **An aid, not a guarantee.** A clean run does not mean your paper meets the venue's rules:
|
|
34
|
+
> presets are transcribed from calls for papers that change, and any check can miss something.
|
|
35
|
+
> Check against the venue's own call and template. Provided "as is", without warranty — see
|
|
36
|
+
> [LICENSE](LICENSE).
|
|
37
|
+
|
|
33
38
|
## Contents
|
|
34
39
|
|
|
35
40
|
- [The pipeline](#-the-pipeline)
|
|
@@ -69,16 +74,17 @@ build: compiles and measures the PDF; lint then checks pages, fonts, references
|
|
|
69
74
|
A **venue preset** holds a venue's format and page limits. A paper's **kind** picks which limit
|
|
70
75
|
applies: at AgenticDev a `short` paper gets 5 pages and a `full` one 10. You choose it once per paper.
|
|
71
76
|
|
|
72
|
-
| preset | venue | format
|
|
73
|
-
| --------------------------- | ------------------- |
|
|
74
|
-
| `paperlint:acm-sigconf` | any ACM conference | ACM two-column
|
|
75
|
-
| `paperlint:agenticdev` | AgenticDev @ ASE | ACM two-column
|
|
76
|
-
| `paperlint:aisec` | AISec @ ACM CCS | ACM two-column
|
|
77
|
-
| `paperlint:realm` | REALM @ EMNLP | ACL two-column, A4
|
|
78
|
-
| `paperlint:ieee-conference` | any IEEE conference | IEEE two-column
|
|
79
|
-
| `paperlint:aidc` | AIDC @ IEEE ACSAC | IEEE two-column, `compsoc`
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
| preset | venue | format | kinds and page limits |
|
|
78
|
+
| --------------------------- | ------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
79
|
+
| `paperlint:acm-sigconf` | any ACM conference | ACM two-column | no kinds: the format only, no page limit |
|
|
80
|
+
| `paperlint:agenticdev` | AgenticDev @ ASE | ACM two-column | `short` 5, `full` 10, `demo` 5 pages, + 2 pages of references |
|
|
81
|
+
| `paperlint:aisec` | AISec @ ACM CCS | ACM two-column | `research`, `benchmark`, `position`, `sok`: 10 pages + 2 pages of references |
|
|
82
|
+
| `paperlint:realm` | REALM @ EMNLP | ACL two-column, A4 | `long` 8, `short` 4 — recorded, not checked ([why](docs/rules.md#checks-against-the-venue)) |
|
|
83
|
+
| `paperlint:ieee-conference` | any IEEE conference | IEEE two-column | no kinds: the format only, no page limit |
|
|
84
|
+
| `paperlint:aidc` | AIDC @ IEEE ACSAC | IEEE two-column, `compsoc` | `regular` 12, `short` 6, counted before the references ([why](docs/rules.md#checks-against-the-venue)); requires the «LLM Usage Statement»; double-blind |
|
|
85
|
+
| `paperlint:msr` | MSR (Technical) | IEEE two-column, no `compsoc` | `technical` 10 pages, appendices included, + 2 pages of only references ([why](docs/rules.md#checks-against-the-venue)); double-blind |
|
|
86
|
+
|
|
87
|
+
Not shipped yet: USENIX, NeurIPS, Springer, IEEE venues other than AIDC and MSR, and ACL venues other than
|
|
82
88
|
REALM.
|
|
83
89
|
[Add yours](#-add-a-venue-that-isnt-listed) in one small file.
|
|
84
90
|
|
|
@@ -198,7 +204,8 @@ Every skill, by stage: [`docs/skills.md`](docs/skills.md).
|
|
|
198
204
|
bibliography usually builds without the network ([`docs/references.md`](docs/references.md)).
|
|
199
205
|
- A paper whose body is in other files (`\input{sections/intro}`) is linted file by file: a finding
|
|
200
206
|
lands at the file and line it is in, and `--fix` edits that file. The files are the ones
|
|
201
|
-
`paper.tex` includes from its body, found where the build finds them — not every `.tex` beside it
|
|
207
|
+
`paper.tex` includes from its body, found where the build finds them — not every `.tex` beside it,
|
|
208
|
+
and not an included `.bbl` (a tool's output, read only as part of the whole paper).
|
|
202
209
|
- Lint also checks the pipeline's own records: `PIPELINE-STATUS.md`, reviews, notes on related
|
|
203
210
|
papers ([`docs/rules.md`](docs/rules.md)).
|
|
204
211
|
- The rules run on ESLint, so a deliberate exception is a comment on the line above:
|
|
@@ -221,6 +228,12 @@ papers/my-paper/paperlint.json one paper: its venue preset ("extends"), its ki
|
|
|
221
228
|
- The paper's venue preset is its `"extends"` key — the one `new --venue` writes.
|
|
222
229
|
- A paper's `paperlint.json` overrides the project's, key by key.
|
|
223
230
|
- **An unknown key is an error**, so a typo cannot silently turn a setting off.
|
|
231
|
+
- A paper that moves between venues keeps a list of attempts, `cycles`, instead of `"extends"`; each
|
|
232
|
+
records its deadlines as read from the portal and the call, plus any override the chairs granted,
|
|
233
|
+
and the earlier reading binds ([`docs/configuration.md`](docs/configuration.md#deadlines)).
|
|
234
|
+
- **Deadlines of a shipped venue preset stay current on their own:** paperlint's repository re-reads
|
|
235
|
+
each preset's portal daily and releases a patch when a date moves, so your normal dependency update
|
|
236
|
+
(Dependabot, Renovate) brings it. `paperlint lint` itself never reads the clock or the network.
|
|
224
237
|
|
|
225
238
|
Every key: [`docs/configuration.md`](docs/configuration.md). Already use ESLint for other files? See
|
|
226
239
|
[`docs/configuration.md`](docs/configuration.md#using-the-rules-from-an-existing-eslint-config).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type DeadlineWhat, type PortalDeadlines } from "../../domain/deadline.ts";
|
|
2
|
+
import { type Result } from "../../domain/result.ts";
|
|
3
|
+
import type { PortalFailure } from "../../domain/submission.ts";
|
|
4
|
+
/** What a listed deadline's label names, in paperlint's words; null for one paperlint does not name. */
|
|
5
|
+
export declare function whatOf(label: string): DeadlineWhat | null;
|
|
6
|
+
/** `GET /deadlines` → the portal's deadlines, or why the page is not them. */
|
|
7
|
+
export declare function parseDeadlinesPage(httpStatus: number, html: string): Result<PortalDeadlines, PortalFailure>;
|
|
8
|
+
//# sourceMappingURL=deadlines.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadlines.d.ts","sourceRoot":"","sources":["../../../src/adapters/hotcrp/deadlines.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AA2BhE,wGAAwG;AACxG,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAOzD;AA+ED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,CAYxC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { VenueDeadlines } from "../../ports/venue-deadlines.ts";
|
|
2
|
+
/** The deadlines of the HotCRP site at `url` (no trailing slash). */
|
|
3
|
+
export declare function hotcrpDeadlines(o: {
|
|
4
|
+
readonly url: string;
|
|
5
|
+
}): VenueDeadlines;
|
|
6
|
+
//# sourceMappingURL=deadlines.io.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadlines.io.d.ts","sourceRoot":"","sources":["../../../src/adapters/hotcrp/deadlines.io.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAKrE,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,CAAC,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAc3E"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HotCRP's deadlines page over Node's `fetch`: `GET <site>/deadlines`, no token — the page is
|
|
3
|
+
* public, and a deadline is nobody's secret.
|
|
4
|
+
*/
|
|
5
|
+
import { messageOf } from "../../domain/text.js";
|
|
6
|
+
import { err } from "../../domain/result.js";
|
|
7
|
+
import { parseDeadlinesPage } from "./deadlines.js";
|
|
8
|
+
const READ_TIMEOUT_MS = 60_000;
|
|
9
|
+
/** The deadlines of the HotCRP site at `url` (no trailing slash). */
|
|
10
|
+
export function hotcrpDeadlines(o) {
|
|
11
|
+
return {
|
|
12
|
+
async read() {
|
|
13
|
+
try {
|
|
14
|
+
const r = await fetch(`${o.url}/deadlines`, {
|
|
15
|
+
headers: { Accept: "text/html" },
|
|
16
|
+
signal: AbortSignal.timeout(READ_TIMEOUT_MS),
|
|
17
|
+
});
|
|
18
|
+
return parseDeadlinesPage(r.status, await r.text());
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
return err({ kind: "unreachable", detail: messageOf(e) });
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=deadlines.io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadlines.io.js","sourceRoot":"","sources":["../../../src/adapters/hotcrp/deadlines.io.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAAC,CAA2B;IACzD,OAAO;QACL,KAAK,CAAC,IAAI;YACR,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE;oBAC1C,OAAO,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE;oBAChC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC;iBAC7C,CAAC,CAAC;gBACH,OAAO,kBAAkB,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACtD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HotCRP's deadlines page (`<site>/deadlines`), parsed at the boundary into paperlint's
|
|
3
|
+
* `PortalDeadlines`. Pure.
|
|
4
|
+
*
|
|
5
|
+
* ── WHAT THE PAGE IS, measured 2026-10-05 on five live sites (`cassette/deadlines-*.html`) ───────
|
|
6
|
+
* The page is HTML, public, no account needed; the API endpoint of the same name (`/api/deadlines`)
|
|
7
|
+
* answers 401 «Missing credentials» without a token. The machine-readable part is the argument of
|
|
8
|
+
* `hotcrp.init_deadlines({…})` in the page's last script: the author's `status_json()`
|
|
9
|
+
* (kohler/hotcrp, src/contact.php), of which an anonymous visitor gets
|
|
10
|
+
*
|
|
11
|
+
* now the server's clock, Unix seconds with a fraction
|
|
12
|
+
* sub.open whether the submission round is open now
|
|
13
|
+
* sub.sub the submission deadline, Unix seconds (SubmissionRound::$submit)
|
|
14
|
+
* sub.reg the registration deadline, when it is earlier (SubmissionRound::$register)
|
|
15
|
+
* sub.blind true | "optional" | "until-review" (Conf::submission_blindness)
|
|
16
|
+
* final.done the final-version (camera-ready) deadline (final_deadline_for_display)
|
|
17
|
+
*
|
|
18
|
+
* The RESUBMISSION deadline (updates allowed until, `SubmissionRound::$resubmit`) is not in that
|
|
19
|
+
* object; the page lists it only in its `<dl>`, each entry a `<dt>` with the deadline's label and a
|
|
20
|
+
* `<span data-ts="…">` holding the same Unix seconds (pages/p_deadlines.php, `dl1`). So both are
|
|
21
|
+
* read: the JSON for what it names, the list for every deadline the page shows, matched by label.
|
|
22
|
+
*/
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
import { instantOfUnix, } from "../../domain/deadline.js";
|
|
25
|
+
import { err, ok } from "../../domain/result.js";
|
|
26
|
+
const Blind = z.union([
|
|
27
|
+
z.boolean(),
|
|
28
|
+
z.literal("optional"),
|
|
29
|
+
z.literal("until-review"),
|
|
30
|
+
]);
|
|
31
|
+
const Status = z.looseObject({
|
|
32
|
+
now: z.number(),
|
|
33
|
+
sub: z
|
|
34
|
+
.looseObject({
|
|
35
|
+
open: z.boolean().optional(),
|
|
36
|
+
sub: z.number().optional(),
|
|
37
|
+
reg: z.number().optional(),
|
|
38
|
+
blind: Blind.optional(),
|
|
39
|
+
})
|
|
40
|
+
.optional(),
|
|
41
|
+
final: z.looseObject({ done: z.number().optional() }).optional(),
|
|
42
|
+
});
|
|
43
|
+
/** The script call that carries the status object; the argument is JSON. */
|
|
44
|
+
const INIT = /hotcrp\.init_deadlines\((\{.*?\})\)/s;
|
|
45
|
+
/** One listed deadline: its label in `<strong>`, its time in `data-ts`. */
|
|
46
|
+
const ENTRY = /<dt><strong>([^<]*)<\/strong>:\s*<span[^>]*\bdata-ts="(\d+)"/g;
|
|
47
|
+
/** The page's title, for a page that is no deadlines page (a site that does not exist). */
|
|
48
|
+
const TITLE = /<h1 id="h-title">([^<]*)</;
|
|
49
|
+
/** What a listed deadline's label names, in paperlint's words; null for one paperlint does not name. */
|
|
50
|
+
export function whatOf(label) {
|
|
51
|
+
const l = label.toLowerCase();
|
|
52
|
+
if (/\bresubmission deadline\b/.test(l))
|
|
53
|
+
return "resubmission";
|
|
54
|
+
if (/\bregistration deadline\b/.test(l))
|
|
55
|
+
return "registration";
|
|
56
|
+
if (/\bsubmission deadline\b/.test(l))
|
|
57
|
+
return "submission";
|
|
58
|
+
if (/\bfinal\b|\bcamera[- ]ready\b/.test(l))
|
|
59
|
+
return "camera-ready";
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function fromStatus(s) {
|
|
63
|
+
const named = [
|
|
64
|
+
["registration", s.sub?.reg],
|
|
65
|
+
["submission", s.sub?.sub],
|
|
66
|
+
["camera-ready", s.final?.done],
|
|
67
|
+
];
|
|
68
|
+
return named.flatMap(([what, at]) => at === undefined ? [] : [{ what, label: what, at: instantOfUnix(at) }]);
|
|
69
|
+
}
|
|
70
|
+
/** Every deadline the page lists, with its label as the page spells it. */
|
|
71
|
+
function fromList(html) {
|
|
72
|
+
return [...html.matchAll(ENTRY)].map((m) => {
|
|
73
|
+
// Both groups always participate in a match of ENTRY: there is no fallback to take.
|
|
74
|
+
const label = String(m[1]).trim();
|
|
75
|
+
return { what: whatOf(label), label, at: instantOfUnix(Number(m[2])) };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** Listed entries first (they carry the page's label); a status entry only when the list lacks its kind. */
|
|
79
|
+
function merged(listed, named) {
|
|
80
|
+
const extra = named.filter((n) => !listed.some((l) => l.what === n.what && l.at === n.at));
|
|
81
|
+
return [...listed, ...extra].toSorted((a, b) => a.at.localeCompare(b.at));
|
|
82
|
+
}
|
|
83
|
+
const refused = (httpStatus, html) => {
|
|
84
|
+
const title = TITLE.exec(html)?.[1]?.trim();
|
|
85
|
+
return {
|
|
86
|
+
kind: "refused",
|
|
87
|
+
httpStatus,
|
|
88
|
+
messages: title === undefined
|
|
89
|
+
? []
|
|
90
|
+
: [{ message: title, field: null, status: null }],
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/** The status object in the page's `init_deadlines` call, or why there is none. */
|
|
94
|
+
function statusOf(httpStatus, html) {
|
|
95
|
+
const m = INIT.exec(html);
|
|
96
|
+
if (m === null)
|
|
97
|
+
return err({
|
|
98
|
+
kind: "malformed",
|
|
99
|
+
httpStatus,
|
|
100
|
+
detail: "the page has no `hotcrp.init_deadlines(…)` call — not a HotCRP deadlines page",
|
|
101
|
+
});
|
|
102
|
+
const json = (() => {
|
|
103
|
+
try {
|
|
104
|
+
return JSON.parse(String(m[1])); // the group always participates in a match of INIT
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
})();
|
|
110
|
+
const parsed = Status.safeParse(json);
|
|
111
|
+
return parsed.success
|
|
112
|
+
? ok(parsed.data)
|
|
113
|
+
: err({
|
|
114
|
+
kind: "malformed",
|
|
115
|
+
httpStatus,
|
|
116
|
+
detail: "the `init_deadlines` argument is not the status object HotCRP documents",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** `GET /deadlines` → the portal's deadlines, or why the page is not them. */
|
|
120
|
+
export function parseDeadlinesPage(httpStatus, html) {
|
|
121
|
+
if (httpStatus < 200 || httpStatus >= 300)
|
|
122
|
+
return err(refused(httpStatus, html));
|
|
123
|
+
const status = statusOf(httpStatus, html);
|
|
124
|
+
if (!status.ok)
|
|
125
|
+
return status;
|
|
126
|
+
const s = status.value;
|
|
127
|
+
return ok({
|
|
128
|
+
now: s.now,
|
|
129
|
+
open: s.sub?.open ?? false,
|
|
130
|
+
blind: s.sub?.blind ?? null,
|
|
131
|
+
deadlines: merged(fromList(html), fromStatus(s)),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=deadlines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadlines.js","sourceRoot":"","sources":["../../../src/adapters/hotcrp/deadlines.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,4 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { CONTENT_FIELD, changeObject, hotcrpPortal, type HotcrpOptions, } from "./portal.io.ts";
|
|
6
6
|
export { parseShow, parseUpdate, sha256OfHash } from "./response.ts";
|
|
7
|
+
export { hotcrpDeadlines } from "./deadlines.io.ts";
|
|
8
|
+
export { parseDeadlinesPage, whatOf } from "./deadlines.ts";
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/hotcrp/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/hotcrp/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -4,4 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { CONTENT_FIELD, changeObject, hotcrpPortal, } from "./portal.io.js";
|
|
6
6
|
export { parseShow, parseUpdate, sha256OfHash } from "./response.js";
|
|
7
|
+
export { hotcrpDeadlines } from "./deadlines.io.js";
|
|
8
|
+
export { parseDeadlinesPage, whatOf } from "./deadlines.js";
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/hotcrp/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,aAAa,EACb,YAAY,EACZ,YAAY,GAEb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/hotcrp/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,aAAa,EACb,YAAY,EACZ,YAAY,GAEb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAS7C,OAAO,EAAE,MAAM,EAAE,MAAM,+CAA+C,CAAC;AAEvE,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAS7C,OAAO,EAAE,MAAM,EAAE,MAAM,+CAA+C,CAAC;AAEvE,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAcpE,OAAO,EAAyB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAiDrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAoBnE,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAGL,IAAI,EAEL,MAAM,WAAW,CAAC;AACnB,OAAO,EAML,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAYxB,OAAO,EAKL,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,IAAI,EAAE,CAAC;AAChB,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAgMtC,4GAA4G;AAC5G,wBAAgB,WAAW,CACzB,IAAI,EAAE,eAAe,YAAK,EAC1B,WAAW,EAAE,OAAO,GACnB,WAAW,EAAE,CA2Lf;AAYD,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,CAG5E;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAG7C,CAAC;AAEF,kGAAkG;AAClG,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACxD;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAY7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,CAa/D;AAQD;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAO9C,CAAC;AAmBF,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAC9C,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBnB;AAaD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,MAAM,CAAC;IAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,GAAG,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC,CAgBnD;AA8ED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,eAAe,CAAC,CAsCzB;AAuBD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAmFvD;AAkDD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,CAAC,EAAE,IAAI,EACP,EACE,GAAiB,EACjB,GAAmB,EACnB,GAAmB,GACpB,GAAE;IACD,GAAG,CAAC,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC;IACzB,GAAG,CAAC,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,UAAU,CAgCZ;AAED,0GAA0G;AAC1G,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAG1D;AAED,0EAA0E;AAC1E,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,MAEK,CAAC;AAEpD,4FAA4F;AAC5F,eAAO,MAAM,WAAW,GAAI,KAAK;IAC/B,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,KAAG,MAAM,EAC2D,CAAC;AAEtE,gGAAgG;AAChG,wBAAgB,OAAO,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAOjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,EACE,GAAmB,EACnB,GAAe,EAUf,OAC8C,GAC/C,GAAE;IACD,GAAG,CAAC,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IAC3B,kGAAkG;IAClG,GAAG,CAAC,EAAE,CACJ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;KAAE,KACnC;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/B,GACL,MAAM,CAiCR;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,EACE,GAAG,EACH,GAAG,EACH,GAAG,EACH,KAAY,EACZ,cAAsB,GACvB,EAAE;IACD,GAAG,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC;IACxB,GAAG,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GACA,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,uFAAuF;AACvF,MAAM,WAAW,WAAY,SAAQ,YAAY;IAC/C,0GAA0G;IAC1G,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACtE,EACE,QAAQ,EACR,GAAG,EACH,WAAW,EACX,GAAG,GACJ,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC;IACrB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC,GACA,OAAO,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CA8CrC;AAwMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAYzD;AAkGD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,IAAI,EACP,EACE,GAAG,EACH,GAAG,EACH,GAAG,GACJ,EAAE;IAAE,GAAG,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC;IAAC,GAAG,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACtE,SAAS,GAAE,OAAO,YAA2B,GAC5C;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,CAAA;CAAE,CAYvE;AAmDD,wBAAsB,GAAG,CACvB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EACE,GAAiB,EACjB,GAAmB,EACnB,GAAmB,EACnB,eAAkC,GACnC,GAAE;IACD,GAAG,CAAC,EAAE,OAAO,OAAO,CAAC,GAAG,CAAC;IACzB,GAAG,CAAC,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CAC9B,GACL,OAAO,CAAC,MAAM,CAAC,CAqLjB"}
|
package/dist/cli.js
CHANGED
|
@@ -37,7 +37,7 @@ import { presetsDir } from "./package-dirs.js";
|
|
|
37
37
|
export { isMain };
|
|
38
38
|
import { checkStructure, formatStructure, asEslintResults, } from "./structure.js";
|
|
39
39
|
import { buildPapers, papersIn, anyFailed, remedyFor, MAIN } from "./build.js";
|
|
40
|
-
import { includeBlocks, paperBodies, unreadLines } from "./paper-includes.js";
|
|
40
|
+
import { FRAGMENT_FILES, includeBlocks, paperBodies, unreadLines, } from "./paper-includes.js";
|
|
41
41
|
import { prepareEngine } from "./build-engine.js";
|
|
42
42
|
import { cacheRoot, cachedTree, runToolchain } from "./toolchain.js";
|
|
43
43
|
import { banalInstaller, parseBanalSettings } from "./adapters/banal/index.js";
|
|
@@ -49,7 +49,7 @@ import { imageSize } from "./adapters/png/index.js";
|
|
|
49
49
|
import { captions } from "./adapters/srt/index.js";
|
|
50
50
|
import { TALK_RULE_LEVELS, talkRules } from "./talk-rules.js";
|
|
51
51
|
import { ANONYMITY_RULE_LEVELS, anonymityRules, PAGE_LIMIT_RULE_LEVELS, pageLimitRules, VENUE_RULE_LEVELS, venueRules, } from "./venue-rules.js";
|
|
52
|
-
import { FORMAT_RULE_LEVELS, formatRules, TEX_VENUE_RULE_LEVELS, texVenueRules, } from "./tex-venue-rules.js";
|
|
52
|
+
import { FORMAT_RULE_LEVELS, formatRules, CYCLE_PORT_RULE_LEVELS, cyclePortRules, TEX_VENUE_RULE_LEVELS, texVenueRules, } from "./tex-venue-rules.js";
|
|
53
53
|
import { claimProvenanceRule } from "./claim-provenance.js";
|
|
54
54
|
import { registerRule } from "./register.js";
|
|
55
55
|
import { REGISTER_BAND_RULE_LEVELS, registerBandRules, } from "./register-bands.js";
|
|
@@ -73,6 +73,8 @@ import { parseRuleBlocks, parseRuleEntries, shippedRuleIds, unknownKeys, } from
|
|
|
73
73
|
export { init };
|
|
74
74
|
export { nextSteps } from "./init.js";
|
|
75
75
|
import paperStages from "#eslint-rules/paper-stages";
|
|
76
|
+
import cycleStage from "#eslint-rules/cycle-stage";
|
|
77
|
+
import { CYCLE_RULE_LEVELS, cycleRules } from "./cycle-rules.js";
|
|
76
78
|
import { FOLDER_VENUE_RULE_LEVELS, folderVenueRules, } from "./folder-venue-rule.js";
|
|
77
79
|
import typography from "#eslint-rules/paper-typography";
|
|
78
80
|
import texBuild from "#eslint-rules/tex-build";
|
|
@@ -184,6 +186,24 @@ settings — paperlint.json, at two levels, one schema. Both are optional.
|
|
|
184
186
|
"extends" names a venue preset: paperlint:<name> (shipped: ${SHIPPED_VENUES().join(", ")})
|
|
185
187
|
or ./path.jsonc, relative to the paperlint.json. npm presets are not supported yet.
|
|
186
188
|
|
|
189
|
+
cycles and deadlines — a paper's attempts at venues, oldest first, instead of "extends",
|
|
190
|
+
"kind" and "submission" (those are then the current attempt's, and refused beside "cycles"):
|
|
191
|
+
|
|
192
|
+
{ "cycles": [ { "id": "msr-2027", "venue": { "kind": "preset", "extends": "paperlint:msr" },
|
|
193
|
+
"kind": "technical", "opened": "2026-10-05",
|
|
194
|
+
"deadlines": [ { "what": "submission",
|
|
195
|
+
"observed": [ { "at": "2026-10-23 AoE", "source": "call",
|
|
196
|
+
"url": "<the call>", "read": "2026-10-05" } ] } ] } ] }
|
|
197
|
+
|
|
198
|
+
A venue preset carries the portal's deadlines, read and dated; the package updates them and
|
|
199
|
+
your dependency update brings them. A paper adds what the call says ("source": "call") and,
|
|
200
|
+
for a deadline moved by the chairs, "override": { "at", "reason", "evidence" } — evidence a
|
|
201
|
+
file in the paper folder. The override wins; otherwise the earlier reading binds. "what":
|
|
202
|
+
registration (the call's abstract), submission (the full paper), resubmission, notification,
|
|
203
|
+
camera-ready. A venue without a preset: "venue": { "kind": "named", "name", "url" }, every
|
|
204
|
+
deadline typed from its call. Lint never reads the clock: it checks the record, not the time
|
|
205
|
+
left. docs/configuration.md#the-papers-cycles
|
|
206
|
+
|
|
187
207
|
"rules" is { "<rule>": "<severity>" } for every paper file in scope, or ESLint flat-config
|
|
188
208
|
blocks (files, ignores, rules) with globs relative to that file. Order, later wins: paperlint's
|
|
189
209
|
own, the venue preset's, the root file's, the paper's. Optional rules (off unless turned on):
|
|
@@ -204,6 +224,20 @@ const REGISTER_AND_CASE_RULES = {
|
|
|
204
224
|
...registerBandRules(TEX_RULE_DEPS),
|
|
205
225
|
"heading-case": headingCaseRule(TEX_RULE_DEPS),
|
|
206
226
|
};
|
|
227
|
+
/**
|
|
228
|
+
* The paper's attempts (`cycles` in paperlint.json), judged on `PIPELINE-STATUS.md`: the record itself
|
|
229
|
+
* and its evidence (src/cycle-rules.ts), and the stages that name a cycle (eslint-rules/cycle-stage.ts).
|
|
230
|
+
* The `cycle` plugin's other rule, `port-done`, reads `paper.tex` (src/tex-venue-rules.ts).
|
|
231
|
+
*/
|
|
232
|
+
/** The markdown language with YAML frontmatter: what every markdown block of the config lints as. */
|
|
233
|
+
const MARKDOWN_WITH_FRONTMATTER = {
|
|
234
|
+
language: "markdown/gfm",
|
|
235
|
+
languageOptions: { frontmatter: "yaml" },
|
|
236
|
+
};
|
|
237
|
+
const CYCLE_STATUS_RULES = {
|
|
238
|
+
...cycleRules({ files: nodeFiles, venuesDir: presetsDir() }),
|
|
239
|
+
...cycleStage.rules,
|
|
240
|
+
};
|
|
207
241
|
/** The rules of the files `paper.tex` includes, each read on its own as a fragment (#144). */
|
|
208
242
|
const FRAGMENT_RULES = {
|
|
209
243
|
"future-promise": texBuild["future-promise"],
|
|
@@ -229,10 +263,7 @@ export function buildConfig(opts = {}, texLanguage) {
|
|
|
229
263
|
"paper/section-word": "warn",
|
|
230
264
|
"paper/leading-zero": "warn",
|
|
231
265
|
};
|
|
232
|
-
const md =
|
|
233
|
-
language: "markdown/gfm",
|
|
234
|
-
languageOptions: { frontmatter: "yaml" },
|
|
235
|
-
};
|
|
266
|
+
const md = MARKDOWN_WITH_FRONTMATTER;
|
|
236
267
|
const cfg = [
|
|
237
268
|
// 🔴 THE PROJECT'S PAPER TEMPLATE IS NOT A PAPER. `paperlint new` reads `<papers>/.template/`, and
|
|
238
269
|
// its files carry every marker a paper does. Flat config does NOT ignore dot-directories by
|
|
@@ -269,12 +300,14 @@ export function buildConfig(opts = {}, texLanguage) {
|
|
|
269
300
|
...folderVenueRules({ files: nodeFiles, venuesDir: presetsDir() }),
|
|
270
301
|
},
|
|
271
302
|
},
|
|
303
|
+
cycle: { rules: CYCLE_STATUS_RULES },
|
|
272
304
|
},
|
|
273
305
|
...md,
|
|
274
306
|
rules: {
|
|
275
307
|
"paper/stages": "error",
|
|
276
308
|
"paper/source": "error",
|
|
277
309
|
...FOLDER_VENUE_RULE_LEVELS,
|
|
310
|
+
...CYCLE_RULE_LEVELS,
|
|
278
311
|
},
|
|
279
312
|
},
|
|
280
313
|
{
|
|
@@ -326,6 +359,7 @@ export function buildConfig(opts = {}, texLanguage) {
|
|
|
326
359
|
...pageLimitRules({ files: nodeFiles, venuesDir: presetsDir() }),
|
|
327
360
|
},
|
|
328
361
|
},
|
|
362
|
+
cycle: { rules: cyclePortRules(TEX_RULE_DEPS) },
|
|
329
363
|
},
|
|
330
364
|
language: "tex/latex",
|
|
331
365
|
rules: {
|
|
@@ -348,6 +382,7 @@ export function buildConfig(opts = {}, texLanguage) {
|
|
|
348
382
|
...TEX_VENUE_RULE_LEVELS,
|
|
349
383
|
...FORMAT_RULE_LEVELS,
|
|
350
384
|
...PAGE_LIMIT_RULE_LEVELS,
|
|
385
|
+
...CYCLE_PORT_RULE_LEVELS,
|
|
351
386
|
// Silent for a paper that declares no `talk` (src/talk-rules.ts), but talk/undeclared.
|
|
352
387
|
...TALK_RULE_LEVELS,
|
|
353
388
|
},
|
|
@@ -361,7 +396,7 @@ export function buildConfig(opts = {}, texLanguage) {
|
|
|
361
396
|
const fragments = texLanguage
|
|
362
397
|
? [
|
|
363
398
|
{
|
|
364
|
-
files: [
|
|
399
|
+
files: [...FRAGMENT_FILES],
|
|
365
400
|
ignores: ["**/paper.tex"],
|
|
366
401
|
plugins: {
|
|
367
402
|
tex: {
|