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
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE CYCLE — one attempt of a paper at one venue: where it goes, as what kind, when the attempt was
|
|
3
|
+
* decided, the venue's deadlines as they were read, which submission on the portal is this paper's,
|
|
4
|
+
* and how the attempt ended. A paper's `paperlint.json` holds them as a LIST, `cycles`, oldest first:
|
|
5
|
+
*
|
|
6
|
+
* {
|
|
7
|
+
* "id": "beta-2027",
|
|
8
|
+
* "venue": { "kind": "preset", "extends": "paperlint:beta" }, // or { "kind": "named", "name", "url" }
|
|
9
|
+
* "kind": "technical",
|
|
10
|
+
* "opened": "2026-09-09",
|
|
11
|
+
* "phase": "porting", // default "prepared"
|
|
12
|
+
* "deadlines": [{ "what": "submission", "observed": [{ "at": "2026-10-23 AoE", "source": "call", "url": "…", "read": "2026-09-09" }] }],
|
|
13
|
+
* "submission": { "id": 7 },
|
|
14
|
+
* "outcome": { "kind": "open" }
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* ── WHY A LIST OF ATTEMPTS, NOT ONE VENUE ───────────────────────────────────────────────
|
|
18
|
+
* A paper goes to a venue, is rejected, and goes to the next one; the settings that every reader
|
|
19
|
+
* takes from the file — `extends`, `kind`, `submission` — are the CURRENT attempt's, and change
|
|
20
|
+
* together when the attempt changes. Held flat, they lag the decision (the preset is switched "when
|
|
21
|
+
* the port lands", weeks later) and the decision itself has no field, so it is written as a comment.
|
|
22
|
+
* Here it is a record: the new attempt is a new entry, the old one gets its `outcome`.
|
|
23
|
+
*
|
|
24
|
+
* ── THE PHASE, and why it is DECLARED ────────────────────────────────────────────────────
|
|
25
|
+
* The day a new venue is chosen, the source is still in the old venue's template. The venue's format
|
|
26
|
+
* checks are then not wrong, but not applicable: a page count under the wrong template says nothing
|
|
27
|
+
* about the new limit. `"phase": "porting"` says the port is open work; the format rules are silent
|
|
28
|
+
* under it, and `cycle/port-done` fires when the class line already matches the venue's template
|
|
29
|
+
* and the phase still says porting. The declaration cannot lie for long in either direction, and it
|
|
30
|
+
* is a record of work, not a softer severity. Absent, the phase is `prepared`.
|
|
31
|
+
*
|
|
32
|
+
* ── THE DEADLINES ARE READINGS ───────────────────────────────────────────────────────────
|
|
33
|
+
* Each deadline records what the portal and the call said, where and when, and an optional human
|
|
34
|
+
* override; the earlier reading binds unless overridden (`deadline.ts`). For a venue with a preset,
|
|
35
|
+
* the portal's readings come from the preset — updated with the package — and the paper adds the
|
|
36
|
+
* call's readings and its overrides on top.
|
|
37
|
+
*
|
|
38
|
+
* Parsed by hand: the domain imports no schema library (see `src/CLAUDE.md`).
|
|
39
|
+
*/
|
|
40
|
+
import { type Result } from "./result.ts";
|
|
41
|
+
import { type Deadline, type IsoDate } from "./deadline.ts";
|
|
42
|
+
/** The venue of an attempt: a preset paperlint resolves, or a venue named by hand (no preset yet). */
|
|
43
|
+
export type CycleVenue = {
|
|
44
|
+
readonly kind: "preset";
|
|
45
|
+
readonly extends: string;
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: "named";
|
|
48
|
+
readonly name: string;
|
|
49
|
+
readonly url: string;
|
|
50
|
+
};
|
|
51
|
+
/** How an attempt ended; `open` while it has not. */
|
|
52
|
+
export type Outcome = {
|
|
53
|
+
readonly kind: "open";
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: "accepted";
|
|
56
|
+
readonly date: IsoDate;
|
|
57
|
+
readonly evidence: string;
|
|
58
|
+
} | {
|
|
59
|
+
readonly kind: "rejected";
|
|
60
|
+
readonly date: IsoDate;
|
|
61
|
+
readonly evidence: string;
|
|
62
|
+
/** A desk rejection (format, scope) — different work follows than after reviews. */
|
|
63
|
+
readonly desk: boolean;
|
|
64
|
+
} | {
|
|
65
|
+
readonly kind: "withdrawn";
|
|
66
|
+
readonly date: IsoDate;
|
|
67
|
+
readonly evidence: string;
|
|
68
|
+
};
|
|
69
|
+
export declare const OUTCOME_KINDS: readonly Outcome["kind"][];
|
|
70
|
+
export type Phase = "porting" | "prepared";
|
|
71
|
+
export interface Cycle {
|
|
72
|
+
/** What a stage refers to (`stages[].cycle`): unique in the list. */
|
|
73
|
+
readonly id: string;
|
|
74
|
+
readonly venue: CycleVenue;
|
|
75
|
+
/** A key of the preset's `format.kinds`; null when the venue has one kind or none is chosen. */
|
|
76
|
+
readonly kind: string | null;
|
|
77
|
+
/** The day this attempt was decided: the rows of the scorecard are judged against it. */
|
|
78
|
+
readonly opened: IsoDate;
|
|
79
|
+
readonly phase: Phase;
|
|
80
|
+
readonly deadlines: readonly Deadline[];
|
|
81
|
+
/** Which submission on the venue's portal is this paper's; null before one exists. */
|
|
82
|
+
readonly submission: {
|
|
83
|
+
readonly id: number;
|
|
84
|
+
} | null;
|
|
85
|
+
readonly outcome: Outcome;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Which attempt the paper is on. An ACCEPTED last cycle is not "parked": the paper lives on at that
|
|
89
|
+
* venue — camera-ready, talk, proceedings — so its venue and kind stay the paper's.
|
|
90
|
+
*/
|
|
91
|
+
export type Current = {
|
|
92
|
+
readonly kind: "cycle";
|
|
93
|
+
readonly cycle: Cycle;
|
|
94
|
+
} | {
|
|
95
|
+
readonly kind: "accepted";
|
|
96
|
+
readonly cycle: Cycle;
|
|
97
|
+
}
|
|
98
|
+
/** The last cycle was rejected or withdrawn and none is open: between venues. */
|
|
99
|
+
| {
|
|
100
|
+
readonly kind: "parked";
|
|
101
|
+
readonly last: Cycle;
|
|
102
|
+
}
|
|
103
|
+
/** `cycles: []`. */
|
|
104
|
+
| {
|
|
105
|
+
readonly kind: "none";
|
|
106
|
+
};
|
|
107
|
+
/** Why a list of cycles names no current attempt. */
|
|
108
|
+
export type CycleProblem = {
|
|
109
|
+
readonly kind: "two-open";
|
|
110
|
+
readonly ids: readonly string[];
|
|
111
|
+
} | {
|
|
112
|
+
readonly kind: "open-not-last";
|
|
113
|
+
readonly id: string;
|
|
114
|
+
} | {
|
|
115
|
+
readonly kind: "duplicate-id";
|
|
116
|
+
readonly id: string;
|
|
117
|
+
};
|
|
118
|
+
/** A `cycles` value → the list, null when absent, or one line naming the entry and the key. Pure. */
|
|
119
|
+
export declare function parseCycles(v: unknown): Result<readonly Cycle[] | null, string>;
|
|
120
|
+
/**
|
|
121
|
+
* The attempt the paper is on. At most one cycle may be open, and it is the last: two open cycles is
|
|
122
|
+
* dual submission or a cycle nobody closed, and both are refused by name. Pure.
|
|
123
|
+
*/
|
|
124
|
+
export declare function currentCycle(cycles: readonly Cycle[]): Result<Current, CycleProblem>;
|
|
125
|
+
/** One line for a problem, naming what to change. Pure. */
|
|
126
|
+
export declare function cycleProblemText(p: CycleProblem): string;
|
|
127
|
+
/** A paper's `cycles`, parsed, with the attempt they name: minted only when the list is consistent. */
|
|
128
|
+
export interface Cycles {
|
|
129
|
+
readonly list: readonly Cycle[];
|
|
130
|
+
readonly current: Current;
|
|
131
|
+
}
|
|
132
|
+
/** The list with its current attempt, or why the list names none. Pure. */
|
|
133
|
+
export declare const cyclesOf: (list: readonly Cycle[]) => Result<Cycles, CycleProblem>;
|
|
134
|
+
/**
|
|
135
|
+
* The cycle whose venue and kind are the paper's: the open attempt, or the accepted one; null when
|
|
136
|
+
* the paper is between venues or has none.
|
|
137
|
+
*/
|
|
138
|
+
export declare const venueCycleOf: (c: Current) => Cycle | null;
|
|
139
|
+
/** The current attempt's own deadline entries; none between venues or before the first attempt. Pure. */
|
|
140
|
+
export declare const currentDeadlines: (c: Current) => readonly Deadline[];
|
|
141
|
+
/**
|
|
142
|
+
* Whether the paper is judged as a blind submission: the venue reviews blind, and this attempt has
|
|
143
|
+
* not been accepted — an accepted paper's camera-ready carries its authors. Pure.
|
|
144
|
+
*/
|
|
145
|
+
export declare const effectiveBlind: (venueBlind: boolean, current: Current) => boolean;
|
|
146
|
+
/** Whether the current attempt declares its port to the venue's template as open work. */
|
|
147
|
+
export declare const isPorting: (current: Current) => boolean;
|
|
148
|
+
//# sourceMappingURL=cycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycle.d.ts","sourceRoot":"","sources":["../../src/domain/cycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAIL,KAAK,QAAQ,EACb,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AAEvB,sGAAsG;AACtG,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,qDAAqD;AACrD,MAAM,MAAM,OAAO,GACf;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AACN,eAAO,MAAM,aAAa,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,EAKnD,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,UAAU,CAAC;AAE3C,MAAM,WAAW,KAAK;IACpB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,gGAAgG;IAChG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;IACxC,sFAAsF;IACtF,QAAQ,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,OAAO,GACf;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GACjD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE;AACtD,iFAAiF;GAC/E;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE;AACnD,oBAAoB;GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9B,qDAAqD;AACrD,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAoN3D,qGAAqG;AACrG,wBAAgB,WAAW,CACzB,CAAC,EAAE,OAAO,GACT,MAAM,CAAC,SAAS,KAAK,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,CAWzC;AAMD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,KAAK,EAAE,GACvB,MAAM,CAAC,OAAO,EAAE,YAAY,CAAC,CAmB/B;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CASxD;AAED,uGAAuG;AACvG,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,KAAK,EAAE,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,2EAA2E;AAC3E,eAAO,MAAM,QAAQ,GACnB,MAAM,SAAS,KAAK,EAAE,KACrB,MAAM,CAAC,MAAM,EAAE,YAAY,CAG7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,OAAO,KAAG,KAAK,GAAG,IAWjD,CAAC;AAEF,yGAAyG;AACzG,eAAO,MAAM,gBAAgB,GAAI,GAAG,OAAO,KAAG,SAAS,QAAQ,EAC7B,CAAC;AAEnC;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,OAAO,EACnB,SAAS,OAAO,KACf,OAAoD,CAAC;AAExD,0FAA0F;AAC1F,eAAO,MAAM,SAAS,GAAI,SAAS,OAAO,KAAG,OACkB,CAAC"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE CYCLE — one attempt of a paper at one venue: where it goes, as what kind, when the attempt was
|
|
3
|
+
* decided, the venue's deadlines as they were read, which submission on the portal is this paper's,
|
|
4
|
+
* and how the attempt ended. A paper's `paperlint.json` holds them as a LIST, `cycles`, oldest first:
|
|
5
|
+
*
|
|
6
|
+
* {
|
|
7
|
+
* "id": "beta-2027",
|
|
8
|
+
* "venue": { "kind": "preset", "extends": "paperlint:beta" }, // or { "kind": "named", "name", "url" }
|
|
9
|
+
* "kind": "technical",
|
|
10
|
+
* "opened": "2026-09-09",
|
|
11
|
+
* "phase": "porting", // default "prepared"
|
|
12
|
+
* "deadlines": [{ "what": "submission", "observed": [{ "at": "2026-10-23 AoE", "source": "call", "url": "…", "read": "2026-09-09" }] }],
|
|
13
|
+
* "submission": { "id": 7 },
|
|
14
|
+
* "outcome": { "kind": "open" }
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* ── WHY A LIST OF ATTEMPTS, NOT ONE VENUE ───────────────────────────────────────────────
|
|
18
|
+
* A paper goes to a venue, is rejected, and goes to the next one; the settings that every reader
|
|
19
|
+
* takes from the file — `extends`, `kind`, `submission` — are the CURRENT attempt's, and change
|
|
20
|
+
* together when the attempt changes. Held flat, they lag the decision (the preset is switched "when
|
|
21
|
+
* the port lands", weeks later) and the decision itself has no field, so it is written as a comment.
|
|
22
|
+
* Here it is a record: the new attempt is a new entry, the old one gets its `outcome`.
|
|
23
|
+
*
|
|
24
|
+
* ── THE PHASE, and why it is DECLARED ────────────────────────────────────────────────────
|
|
25
|
+
* The day a new venue is chosen, the source is still in the old venue's template. The venue's format
|
|
26
|
+
* checks are then not wrong, but not applicable: a page count under the wrong template says nothing
|
|
27
|
+
* about the new limit. `"phase": "porting"` says the port is open work; the format rules are silent
|
|
28
|
+
* under it, and `cycle/port-done` fires when the class line already matches the venue's template
|
|
29
|
+
* and the phase still says porting. The declaration cannot lie for long in either direction, and it
|
|
30
|
+
* is a record of work, not a softer severity. Absent, the phase is `prepared`.
|
|
31
|
+
*
|
|
32
|
+
* ── THE DEADLINES ARE READINGS ───────────────────────────────────────────────────────────
|
|
33
|
+
* Each deadline records what the portal and the call said, where and when, and an optional human
|
|
34
|
+
* override; the earlier reading binds unless overridden (`deadline.ts`). For a venue with a preset,
|
|
35
|
+
* the portal's readings come from the preset — updated with the package — and the paper adds the
|
|
36
|
+
* call's readings and its overrides on top.
|
|
37
|
+
*
|
|
38
|
+
* Parsed by hand: the domain imports no schema library (see `src/CLAUDE.md`).
|
|
39
|
+
*/
|
|
40
|
+
import { err, ok } from "./result.js";
|
|
41
|
+
import { fieldOf, isRecord } from "./record.js";
|
|
42
|
+
import { isIsoDate, parseDeadlines, unknownKey, } from "./deadline.js";
|
|
43
|
+
export const OUTCOME_KINDS = [
|
|
44
|
+
"open",
|
|
45
|
+
"accepted",
|
|
46
|
+
"rejected",
|
|
47
|
+
"withdrawn",
|
|
48
|
+
];
|
|
49
|
+
// ── parsing ────────────────────────────────────────────────────────────────────────────
|
|
50
|
+
const KNOWN_KEYS = [
|
|
51
|
+
"id",
|
|
52
|
+
"venue",
|
|
53
|
+
"kind",
|
|
54
|
+
"opened",
|
|
55
|
+
"phase",
|
|
56
|
+
"deadlines",
|
|
57
|
+
"submission",
|
|
58
|
+
"outcome",
|
|
59
|
+
];
|
|
60
|
+
const where = (i) => `cycles[${String(i)}]`;
|
|
61
|
+
const nonEmpty = (v) => typeof v === "string" && v !== "";
|
|
62
|
+
const VENUE_KEYS = {
|
|
63
|
+
preset: ["kind", "extends"],
|
|
64
|
+
named: ["kind", "name", "url"],
|
|
65
|
+
};
|
|
66
|
+
/** The fields of a venue of `kind`, when they are there. */
|
|
67
|
+
function venueFields(kind, v) {
|
|
68
|
+
const { extends: ext, name, url } = v;
|
|
69
|
+
switch (kind) {
|
|
70
|
+
case "preset":
|
|
71
|
+
return nonEmpty(ext) ? { kind, extends: ext } : null;
|
|
72
|
+
case "named":
|
|
73
|
+
return nonEmpty(name) && nonEmpty(url) ? { kind, name, url } : null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** The venue's own keys refused when unknown, then its shape; null when the kind is neither. */
|
|
77
|
+
function venueShape(v, at) {
|
|
78
|
+
const kind = v["kind"];
|
|
79
|
+
if (kind !== "preset" && kind !== "named")
|
|
80
|
+
return null;
|
|
81
|
+
const unknown = unknownKey(v, VENUE_KEYS[kind], `${at}.venue`, `a ${kind} venue's`);
|
|
82
|
+
if (unknown !== null)
|
|
83
|
+
return err(unknown);
|
|
84
|
+
const venue = venueFields(kind, v);
|
|
85
|
+
return venue === null ? null : ok(venue);
|
|
86
|
+
}
|
|
87
|
+
function parseVenue(v, at) {
|
|
88
|
+
const shaped = isRecord(v) ? venueShape(v, at) : null;
|
|
89
|
+
return (shaped ??
|
|
90
|
+
err(`${at}: "venue" must be { "kind": "preset", "extends": … } or { "kind": "named", "name": …, "url": … }`));
|
|
91
|
+
}
|
|
92
|
+
const isOutcomeKind = (v) => OUTCOME_KINDS.some((k) => k === v);
|
|
93
|
+
/** A decision's shape by kind: `rejected` also says whether it was a desk rejection. */
|
|
94
|
+
const decisionOf = (kind, decided, desk, at) => {
|
|
95
|
+
switch (kind) {
|
|
96
|
+
case "accepted":
|
|
97
|
+
return ok({ kind, ...decided });
|
|
98
|
+
case "withdrawn":
|
|
99
|
+
return ok({ kind, ...decided });
|
|
100
|
+
case "rejected":
|
|
101
|
+
return typeof desk === "boolean" || desk === undefined
|
|
102
|
+
? ok({ kind, ...decided, desk: desk === true })
|
|
103
|
+
: err(`${at}: "outcome.desk" must be true or false`);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const OUTCOME_KEYS = {
|
|
107
|
+
open: ["kind"],
|
|
108
|
+
accepted: ["kind", "date", "evidence"],
|
|
109
|
+
rejected: ["kind", "date", "evidence", "desk"],
|
|
110
|
+
withdrawn: ["kind", "date", "evidence"],
|
|
111
|
+
};
|
|
112
|
+
function parseOutcome(v, at) {
|
|
113
|
+
const kind = fieldOf(v, "kind");
|
|
114
|
+
if (!isRecord(v) || !isOutcomeKind(kind))
|
|
115
|
+
return err(`${at}: "outcome" kind must be one of ${OUTCOME_KINDS.join(", ")}, got ${JSON.stringify(kind)}`);
|
|
116
|
+
const unknown = unknownKey(v, OUTCOME_KEYS[kind], `${at}.outcome`, `${kind === "open" || kind === "accepted" ? "an" : "a"} ${kind} outcome's`);
|
|
117
|
+
if (unknown !== null)
|
|
118
|
+
return err(unknown);
|
|
119
|
+
if (kind === "open")
|
|
120
|
+
return ok({ kind });
|
|
121
|
+
const date = v["date"];
|
|
122
|
+
if (!isIsoDate(date))
|
|
123
|
+
return err(`${at}: "outcome" of kind ${kind} needs "date" (YYYY-MM-DD)`);
|
|
124
|
+
const evidence = v["evidence"];
|
|
125
|
+
if (typeof evidence !== "string" || evidence === "")
|
|
126
|
+
return err(`${at}: "outcome" of kind ${kind} needs "evidence": a file in the paper folder (the decision mail, the reviews, the note that records the withdrawal)`);
|
|
127
|
+
return decisionOf(kind, { date, evidence }, v["desk"], at);
|
|
128
|
+
}
|
|
129
|
+
function parseSubmission(v, at) {
|
|
130
|
+
if (v === undefined || v === null)
|
|
131
|
+
return ok(null);
|
|
132
|
+
const unknown = isRecord(v)
|
|
133
|
+
? unknownKey(v, ["id"], `${at}.submission`, "a submission's")
|
|
134
|
+
: null;
|
|
135
|
+
if (unknown !== null)
|
|
136
|
+
return err(unknown);
|
|
137
|
+
const id = fieldOf(v, "id");
|
|
138
|
+
return typeof id === "number" && Number.isInteger(id) && id > 0
|
|
139
|
+
? ok({ id })
|
|
140
|
+
: err(`${at}: "submission" must be { "id": <the submission number on the venue's portal> }`);
|
|
141
|
+
}
|
|
142
|
+
const isPhase = (v) => v === "porting" || v === "prepared";
|
|
143
|
+
/** `kind`: a string, or null when absent — the venue has one kind, or none is chosen yet. */
|
|
144
|
+
const kindOf = (v) => v === undefined || v === null
|
|
145
|
+
? ok(null)
|
|
146
|
+
: typeof v === "string"
|
|
147
|
+
? ok(v)
|
|
148
|
+
: err('"kind" must be a string or null');
|
|
149
|
+
function parseHead(v, at) {
|
|
150
|
+
const id = v["id"];
|
|
151
|
+
if (typeof id !== "string" || id === "")
|
|
152
|
+
return err(`${at}: "id" must be a non-empty string (what stages refer to)`);
|
|
153
|
+
const venue = parseVenue(v["venue"], at);
|
|
154
|
+
if (!venue.ok)
|
|
155
|
+
return venue;
|
|
156
|
+
const kind = kindOf(v["kind"]);
|
|
157
|
+
if (!kind.ok)
|
|
158
|
+
return err(`${at}: ${kind.error}`);
|
|
159
|
+
const opened = v["opened"];
|
|
160
|
+
if (!isIsoDate(opened))
|
|
161
|
+
return err(`${at}: "opened" must be a date YYYY-MM-DD — the day this attempt was decided`);
|
|
162
|
+
const phase = v["phase"] ?? "prepared";
|
|
163
|
+
if (!isPhase(phase))
|
|
164
|
+
return err(`${at}: "phase" must be "porting" or "prepared"`);
|
|
165
|
+
return ok({
|
|
166
|
+
id,
|
|
167
|
+
venue: venue.value,
|
|
168
|
+
kind: kind.value,
|
|
169
|
+
opened,
|
|
170
|
+
phase,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function parseTail(v, at) {
|
|
174
|
+
const deadlines = parseDeadlines(v["deadlines"], at);
|
|
175
|
+
if (!deadlines.ok)
|
|
176
|
+
return deadlines;
|
|
177
|
+
const submission = parseSubmission(v["submission"], at);
|
|
178
|
+
if (!submission.ok)
|
|
179
|
+
return submission;
|
|
180
|
+
const outcome = parseOutcome(v["outcome"] ?? { kind: "open" }, at);
|
|
181
|
+
if (!outcome.ok)
|
|
182
|
+
return outcome;
|
|
183
|
+
return ok({
|
|
184
|
+
deadlines: deadlines.value,
|
|
185
|
+
submission: submission.value,
|
|
186
|
+
outcome: outcome.value,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function parseCycle(v, i) {
|
|
190
|
+
const at = where(i);
|
|
191
|
+
if (!isRecord(v))
|
|
192
|
+
return err(`${at} must be an object`);
|
|
193
|
+
const unknown = Object.keys(v).find((k) => !KNOWN_KEYS.includes(k));
|
|
194
|
+
if (unknown !== undefined)
|
|
195
|
+
return err(`${at}: unknown key "${unknown}" — known keys: ${KNOWN_KEYS.join(", ")}`);
|
|
196
|
+
const head = parseHead(v, at);
|
|
197
|
+
if (!head.ok)
|
|
198
|
+
return head;
|
|
199
|
+
const tail = parseTail(v, at);
|
|
200
|
+
return tail.ok ? ok({ ...head.value, ...tail.value }) : tail;
|
|
201
|
+
}
|
|
202
|
+
/** A `cycles` value → the list, null when absent, or one line naming the entry and the key. Pure. */
|
|
203
|
+
export function parseCycles(v) {
|
|
204
|
+
if (v === undefined)
|
|
205
|
+
return ok(null);
|
|
206
|
+
if (!Array.isArray(v))
|
|
207
|
+
return err(`"cycles" must be a LIST of attempts, oldest first — a paper can go to several venues`);
|
|
208
|
+
return v.reduce((acc, c, i) => {
|
|
209
|
+
if (!acc.ok)
|
|
210
|
+
return acc;
|
|
211
|
+
const p = parseCycle(c, i);
|
|
212
|
+
return p.ok ? ok([...acc.value, p.value]) : p;
|
|
213
|
+
}, ok([]));
|
|
214
|
+
}
|
|
215
|
+
// ── deriving ───────────────────────────────────────────────────────────────────────────
|
|
216
|
+
const isOpen = (c) => c.outcome.kind === "open";
|
|
217
|
+
/**
|
|
218
|
+
* The attempt the paper is on. At most one cycle may be open, and it is the last: two open cycles is
|
|
219
|
+
* dual submission or a cycle nobody closed, and both are refused by name. Pure.
|
|
220
|
+
*/
|
|
221
|
+
export function currentCycle(cycles) {
|
|
222
|
+
const dup = cycles.find((c, i) => cycles.findIndex((d) => d.id === c.id) !== i);
|
|
223
|
+
if (dup !== undefined)
|
|
224
|
+
return err({ kind: "duplicate-id", id: dup.id });
|
|
225
|
+
const open = cycles.filter(isOpen);
|
|
226
|
+
if (open.length > 1)
|
|
227
|
+
return err({ kind: "two-open", ids: open.map((c) => c.id) });
|
|
228
|
+
const last = cycles.at(-1);
|
|
229
|
+
if (last === undefined)
|
|
230
|
+
return ok({ kind: "none" });
|
|
231
|
+
const [only] = open;
|
|
232
|
+
if (only !== undefined && only !== last)
|
|
233
|
+
return err({ kind: "open-not-last", id: only.id });
|
|
234
|
+
if (isOpen(last))
|
|
235
|
+
return ok({ kind: "cycle", cycle: last });
|
|
236
|
+
return ok(last.outcome.kind === "accepted"
|
|
237
|
+
? { kind: "accepted", cycle: last }
|
|
238
|
+
: { kind: "parked", last });
|
|
239
|
+
}
|
|
240
|
+
/** One line for a problem, naming what to change. Pure. */
|
|
241
|
+
export function cycleProblemText(p) {
|
|
242
|
+
switch (p.kind) {
|
|
243
|
+
case "two-open":
|
|
244
|
+
return `cycles ${p.ids.map((id) => `«${id}»`).join(" and ")} are both open — a paper is on one attempt at a time; two open attempts at two venues is dual submission. Close the one that ended: "outcome": { "kind": "rejected" | "withdrawn" | "accepted", "date", "evidence" }`;
|
|
245
|
+
case "open-not-last":
|
|
246
|
+
return `cycle «${p.id}» is open but is not the last entry — cycles are listed oldest first, and the open one is the current attempt`;
|
|
247
|
+
case "duplicate-id":
|
|
248
|
+
return `two cycles carry the id «${p.id}» — a stage's \`cycle:\` could name either; give each attempt its own id`;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/** The list with its current attempt, or why the list names none. Pure. */
|
|
252
|
+
export const cyclesOf = (list) => {
|
|
253
|
+
const current = currentCycle(list);
|
|
254
|
+
return current.ok ? ok({ list, current: current.value }) : current;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* The cycle whose venue and kind are the paper's: the open attempt, or the accepted one; null when
|
|
258
|
+
* the paper is between venues or has none.
|
|
259
|
+
*/
|
|
260
|
+
export const venueCycleOf = (c) => {
|
|
261
|
+
switch (c.kind) {
|
|
262
|
+
case "cycle":
|
|
263
|
+
return c.cycle;
|
|
264
|
+
case "accepted":
|
|
265
|
+
return c.cycle;
|
|
266
|
+
case "parked":
|
|
267
|
+
return null;
|
|
268
|
+
case "none":
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
/** The current attempt's own deadline entries; none between venues or before the first attempt. Pure. */
|
|
273
|
+
export const currentDeadlines = (c) => venueCycleOf(c)?.deadlines ?? [];
|
|
274
|
+
/**
|
|
275
|
+
* Whether the paper is judged as a blind submission: the venue reviews blind, and this attempt has
|
|
276
|
+
* not been accepted — an accepted paper's camera-ready carries its authors. Pure.
|
|
277
|
+
*/
|
|
278
|
+
export const effectiveBlind = (venueBlind, current) => venueBlind && current.kind !== "accepted";
|
|
279
|
+
/** Whether the current attempt declares its port to the venue's template as open work. */
|
|
280
|
+
export const isPorting = (current) => current.kind === "cycle" && current.cycle.phase === "porting";
|
|
281
|
+
//# sourceMappingURL=cycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycle.js","sourceRoot":"","sources":["../../src/domain/cycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A VENUE'S DEADLINE — what each page said about it, and which instant binds.
|
|
3
|
+
*
|
|
4
|
+
* One entry per deadline of an attempt, in a cycle's `deadlines`:
|
|
5
|
+
*
|
|
6
|
+
* {
|
|
7
|
+
* "what": "submission",
|
|
8
|
+
* "observed": [
|
|
9
|
+
* { "at": "2026-10-20T04:00:00Z", "source": "portal", "url": "…/deadlines", "read": "2026-10-05" },
|
|
10
|
+
* { "at": "2026-10-23 AoE", "source": "call", "url": "…/cfp", "read": "2026-10-05" }
|
|
11
|
+
* ],
|
|
12
|
+
* "override": { "at": "2026-10-27T04:00:00Z", "reason": "…", "evidence": "mail/extension.eml" }
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* ── OBSERVED, THEN OVERRIDDEN ──────────────────────────────────────────────────────────
|
|
16
|
+
* A READING is what one page said on one day: its instant, whether the page was the portal (which
|
|
17
|
+
* refuses the upload) or the call (which only announces), where, and when it was read. The portal
|
|
18
|
+
* and the call often disagree — an AoE day in a call is a day after the portal's UTC instant, and a
|
|
19
|
+
* HotCRP "submissions must be completed by" can fall on the call's abstract day — so both are kept,
|
|
20
|
+
* and the EARLIER binds: the safe reading of two. An OVERRIDE is a human decision that another
|
|
21
|
+
* instant binds (the chairs extended by mail); it wins, and it carries its reason and a file as
|
|
22
|
+
* evidence, so it is never a silent edit of a reading.
|
|
23
|
+
*
|
|
24
|
+
* ── THE WORDS ───────────────────────────────────────────────────────────────────────────
|
|
25
|
+
* A call says "abstract" and "paper"; HotCRP says registration, submission and resubmission. Here:
|
|
26
|
+
* `registration` is the abstract registration (a call's "abstract" date), `submission` the full paper
|
|
27
|
+
* (a call's "paper" date, HotCRP's "submissions must be completed by"), `resubmission` the updates a
|
|
28
|
+
* portal allows to a completed submission. Recorded honestly, a call's "paper" day and a portal's
|
|
29
|
+
* earlier "submission" cutoff are two readings of ONE deadline, and the earlier binds.
|
|
30
|
+
*
|
|
31
|
+
* Parsed by hand: the domain imports no schema library (see `src/CLAUDE.md`). Every object in the
|
|
32
|
+
* record refuses a key it does not know: an `override` typed under another name would otherwise look
|
|
33
|
+
* recorded and not be in force.
|
|
34
|
+
*/
|
|
35
|
+
import { type Result } from "./result.ts";
|
|
36
|
+
/** A calendar day, `YYYY-MM-DD`. */
|
|
37
|
+
export type IsoDate = string;
|
|
38
|
+
/** A moment, as an ISO string in UTC: `2026-10-20T04:00:00Z`. Never a bare date. */
|
|
39
|
+
export type Instant = string;
|
|
40
|
+
export type DeadlineWhat = "registration" | "submission" | "resubmission" | "notification" | "camera-ready";
|
|
41
|
+
/** The kinds, in the order a venue's deadlines keep. */
|
|
42
|
+
export declare const DEADLINE_WHATS: readonly DeadlineWhat[];
|
|
43
|
+
/** What each kind is, in a call's words where they differ — said in every message that names the kinds. */
|
|
44
|
+
export declare const WHAT_MEANS: Readonly<Record<DeadlineWhat, string | null>>;
|
|
45
|
+
export type DeadlineSource = "portal" | "call";
|
|
46
|
+
/** What one page said about one deadline, and on which day it was read. */
|
|
47
|
+
export interface Observation {
|
|
48
|
+
readonly at: Instant;
|
|
49
|
+
readonly source: DeadlineSource;
|
|
50
|
+
/** The page it was read on. */
|
|
51
|
+
readonly url: string;
|
|
52
|
+
readonly read: IsoDate;
|
|
53
|
+
}
|
|
54
|
+
/** A human decision that another instant binds, with why and the file it rests on. */
|
|
55
|
+
export interface Override {
|
|
56
|
+
readonly at: Instant;
|
|
57
|
+
readonly reason: string;
|
|
58
|
+
/** A file in the paper folder: the mail, the saved page. `cycle/evidence` requires it on disk. */
|
|
59
|
+
readonly evidence: string;
|
|
60
|
+
}
|
|
61
|
+
/** One deadline of an attempt, as the paper records it. */
|
|
62
|
+
export interface Deadline {
|
|
63
|
+
readonly what: DeadlineWhat;
|
|
64
|
+
readonly observed: readonly Observation[];
|
|
65
|
+
readonly override: Override | null;
|
|
66
|
+
}
|
|
67
|
+
/** One reading of one deadline, as a venue preset carries it. */
|
|
68
|
+
export interface Reading extends Observation {
|
|
69
|
+
readonly what: DeadlineWhat;
|
|
70
|
+
}
|
|
71
|
+
/** What binds a deadline: the override, or the reading that was earliest. */
|
|
72
|
+
export type Binding = {
|
|
73
|
+
readonly kind: "override";
|
|
74
|
+
readonly override: Override;
|
|
75
|
+
} | {
|
|
76
|
+
readonly kind: "reading";
|
|
77
|
+
readonly reading: Observation;
|
|
78
|
+
};
|
|
79
|
+
/** A deadline in force: its instant, what set it, and every reading of it. */
|
|
80
|
+
export interface InForce {
|
|
81
|
+
readonly what: DeadlineWhat;
|
|
82
|
+
readonly at: Instant;
|
|
83
|
+
readonly by: Binding;
|
|
84
|
+
readonly readings: readonly Observation[];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* What a venue's portal says about its deadlines, in paperlint's words — what the scheduled refresh
|
|
88
|
+
* of the shipped presets reads (`scripts/refresh-deadlines.ts`). One adapter per portal kind.
|
|
89
|
+
*/
|
|
90
|
+
export interface PortalDeadlines {
|
|
91
|
+
/** The portal's clock when it answered, Unix seconds. */
|
|
92
|
+
readonly now: number;
|
|
93
|
+
/** Whether the portal takes submissions now. */
|
|
94
|
+
readonly open: boolean;
|
|
95
|
+
/** How the venue reviews, as the portal says it; null when it does not say. */
|
|
96
|
+
readonly blind: boolean | "optional" | "until-review" | null;
|
|
97
|
+
readonly deadlines: readonly PortalDeadline[];
|
|
98
|
+
}
|
|
99
|
+
/** One deadline the portal lists: its kind when it is one paperlint names, the portal's own label, and when. */
|
|
100
|
+
export interface PortalDeadline {
|
|
101
|
+
readonly what: DeadlineWhat | null;
|
|
102
|
+
readonly label: string;
|
|
103
|
+
readonly at: Instant;
|
|
104
|
+
}
|
|
105
|
+
export declare const isIsoDate: (v: unknown) => v is IsoDate;
|
|
106
|
+
/** Unix seconds → the instant, in UTC. */
|
|
107
|
+
export declare const instantOfUnix: (seconds: number) => Instant;
|
|
108
|
+
/**
|
|
109
|
+
* An ISO instant with its zone (`Z` or `±hh:mm`), or a call's `YYYY-MM-DD AoE`; normalised to UTC.
|
|
110
|
+
* A bare date or a zoneless time is refused: it is the deadline people get wrong.
|
|
111
|
+
*/
|
|
112
|
+
export declare function instantOf(text: string): Result<Instant, string>;
|
|
113
|
+
/** The first key of `v` that is not in `known`, as the refusal naming it; null when there is none. */
|
|
114
|
+
export declare const unknownKey: (v: Readonly<Record<string, unknown>>, known: readonly string[], at: string, whose: string) => string | null;
|
|
115
|
+
/** A cycle's `deadlines` value → the list, or one line naming the place and the cure. Pure. */
|
|
116
|
+
export declare function parseDeadlines(v: unknown, at: string): Result<readonly Deadline[], string>;
|
|
117
|
+
/**
|
|
118
|
+
* Every deadline in force for an attempt: the paper's own entries over the venue preset's readings
|
|
119
|
+
* (empty for a venue without a preset). The override, else the earliest reading. Pure.
|
|
120
|
+
*/
|
|
121
|
+
export declare function deadlinesInForce(own: readonly Deadline[], preset: readonly Reading[]): readonly InForce[];
|
|
122
|
+
/**
|
|
123
|
+
* The deadlines out of order WITHIN one source — the portal's readings, the call's, the overrides —
|
|
124
|
+
* the shape a date typed wrong or an AoE day miscounted takes. Across sources a disagreement is what
|
|
125
|
+
* the record holds, and the earlier binds; it is not a problem. Pure.
|
|
126
|
+
*/
|
|
127
|
+
export declare function deadlineOrderProblems(own: readonly Deadline[], preset: readonly Reading[]): readonly string[];
|
|
128
|
+
//# sourceMappingURL=deadline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadline.d.ts","sourceRoot":"","sources":["../../src/domain/deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAGnD,oCAAoC;AACpC,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAC7B,oFAAoF;AACpF,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,MAAM,MAAM,YAAY,GACpB,cAAc,GACd,YAAY,GACZ,cAAc,GACd,cAAc,GACd,cAAc,CAAC;AACnB,wDAAwD;AACxD,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAMjD,CAAC;AAEF,2GAA2G;AAC3G,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC,CAMpE,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE/C,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,+BAA+B;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED,sFAAsF;AACtF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kGAAkG;IAClG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,2DAA2D;AAC3D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;CACpC;AAED,iEAAiE;AACjE,MAAM,WAAW,OAAQ,SAAQ,WAAW;IAC1C,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;CAC7B;AAED,6EAA6E;AAC7E,MAAM,MAAM,OAAO,GACf;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAEhE,8EAA8E;AAC9E,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC3C;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,cAAc,GAAG,IAAI,CAAC;IAC7D,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;CAC/C;AAED,gHAAgH;AAChH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;CACtB;AAKD,eAAO,MAAM,SAAS,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,OAC+B,CAAC;AAK5E,0CAA0C;AAC1C,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,OACrB,CAAC;AAK5B;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAe/D;AAID,sGAAsG;AACtG,eAAO,MAAM,UAAU,GACrB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACpC,OAAO,SAAS,MAAM,EAAE,EACxB,IAAI,MAAM,EACV,OAAO,MAAM,KACZ,MAAM,GAAG,IAKX,CAAC;AA4JF,+FAA+F;AAC/F,wBAAgB,cAAc,CAC5B,CAAC,EAAE,OAAO,EACV,EAAE,EAAE,MAAM,GACT,MAAM,CAAC,SAAS,QAAQ,EAAE,EAAE,MAAM,CAAC,CAOrC;AA0CD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,SAAS,QAAQ,EAAE,EACxB,MAAM,EAAE,SAAS,OAAO,EAAE,GACzB,SAAS,OAAO,EAAE,CASpB;AAuCD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,SAAS,QAAQ,EAAE,EACxB,MAAM,EAAE,SAAS,OAAO,EAAE,GACzB,SAAS,MAAM,EAAE,CAkBnB"}
|