paperlint 12.0.0 → 13.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/CONTRIBUTING.md +2 -1
- package/dist/adapters/memory/index.d.ts +5 -0
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +16 -0
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/node/index.d.ts +1 -0
- package/dist/adapters/node/index.d.ts.map +1 -1
- package/dist/adapters/node/index.js +1 -0
- package/dist/adapters/node/index.js.map +1 -1
- package/dist/adapters/node/move-file.io.d.ts +4 -0
- package/dist/adapters/node/move-file.io.d.ts.map +1 -0
- package/dist/adapters/node/move-file.io.js +12 -0
- package/dist/adapters/node/move-file.io.js.map +1 -0
- package/dist/adapters/yaml/index.d.ts +3 -0
- package/dist/adapters/yaml/index.d.ts.map +1 -0
- package/dist/adapters/yaml/index.js +17 -0
- package/dist/adapters/yaml/index.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +48 -7
- package/dist/cli.js.map +1 -1
- package/dist/cycle-rules.d.ts +3 -3
- package/dist/cycle-rules.d.ts.map +1 -1
- package/dist/cycle-rules.js +1 -2
- package/dist/cycle-rules.js.map +1 -1
- package/dist/domain/cycle.d.ts +16 -2
- package/dist/domain/cycle.d.ts.map +1 -1
- package/dist/domain/cycle.js +21 -42
- package/dist/domain/cycle.js.map +1 -1
- package/dist/domain/paper-layout.d.ts.map +1 -1
- package/dist/domain/paper-layout.js +5 -2
- package/dist/domain/paper-layout.js.map +1 -1
- package/dist/domain/paths.d.ts +12 -0
- package/dist/domain/paths.d.ts.map +1 -1
- package/dist/domain/paths.js +22 -1
- package/dist/domain/paths.js.map +1 -1
- package/dist/domain/scaffold-layout.js +1 -1
- package/dist/domain/versions-migration.d.ts +51 -0
- package/dist/domain/versions-migration.d.ts.map +1 -0
- package/dist/domain/versions-migration.js +252 -0
- package/dist/domain/versions-migration.js.map +1 -0
- package/dist/domain/versions.d.ts +167 -0
- package/dist/domain/versions.d.ts.map +1 -0
- package/dist/domain/versions.js +457 -0
- package/dist/domain/versions.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +7 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -1
- package/dist/eslint-rules/paper-stages.js +62 -7
- package/dist/eslint-rules/paper-stages.js.map +1 -1
- package/dist/eslint-rules/rule-context.d.ts +2 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -1
- package/dist/ports/move-file.d.ts +7 -0
- package/dist/ports/move-file.d.ts.map +1 -0
- package/dist/ports/move-file.js +2 -0
- package/dist/ports/move-file.js.map +1 -0
- package/dist/ports/yaml.d.ts +5 -0
- package/dist/ports/yaml.d.ts.map +1 -0
- package/dist/ports/yaml.js +2 -0
- package/dist/ports/yaml.js.map +1 -0
- package/dist/submission.d.ts +2 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +49 -9
- package/dist/submission.js.map +1 -1
- package/dist/version-rules.d.ts +16 -0
- package/dist/version-rules.d.ts.map +1 -0
- package/dist/version-rules.js +201 -0
- package/dist/version-rules.js.map +1 -0
- package/dist/versions-command.d.ts +37 -0
- package/dist/versions-command.d.ts.map +1 -0
- package/dist/versions-command.js +209 -0
- package/dist/versions-command.js.map +1 -0
- package/dist/versions.d.ts +76 -0
- package/dist/versions.d.ts.map +1 -0
- package/dist/versions.js +164 -0
- package/dist/versions.js.map +1 -0
- package/docs/configuration.md +76 -2
- package/docs/layout.md +5 -5
- package/docs/rules/cycle/camera-ready-source.md +62 -0
- package/docs/rules/cycle/draft-drift.md +47 -0
- package/docs/rules/cycle/port-done.md +2 -2
- package/docs/rules/cycle/record.md +2 -1
- package/docs/rules/cycle/submitted-frozen.md +65 -0
- package/docs/rules/paper/allowed-files-only.md +1 -1
- package/docs/rules/versions/layout.md +88 -0
- package/docs/rules.md +25 -11
- package/docs/submission.md +21 -3
- package/eslint-rules/paper-stages.test.mjs +62 -0
- package/eslint-rules/paper-stages.ts +67 -8
- package/eslint-rules/prereg-coverage.test.ts +1 -0
- package/eslint-rules/rule-context.ts +2 -0
- package/package.json +2 -1
- package/presets/agent.jsonc +75 -0
- package/presets/agent.md +70 -0
- package/presets/agentverify.jsonc +72 -0
- package/presets/agentverify.md +85 -0
- package/presets/ieee-conference.md +1 -1
- package/presets/nlbse.jsonc +76 -0
- package/presets/nlbse.md +85 -0
- package/scripts/coverage-config.test.ts +3 -0
- package/skills/CLAUDE.md +2 -2
- package/skills/CLAUDE.md.spec.ts +1 -1
- package/skills/paper-pipeline/references/pipeline-status-template.md +4 -1
- package/skills/submit-paper/SKILL.md +18 -10
- package/skills/submit-paper/SKILL.md.spec.ts +17 -9
- package/skills/submit-paper/references/publishers/acm.md +5 -3
- package/src/adapters/memory/index.test.ts +22 -0
- package/src/adapters/memory/index.ts +21 -0
- package/src/adapters/node/index.ts +1 -0
- package/src/adapters/node/move-file.io.ts +21 -0
- package/src/adapters/node/move-file.test.ts +51 -0
- package/src/adapters/yaml/index.test.ts +16 -0
- package/src/adapters/yaml/index.ts +17 -0
- package/src/cli.test.ts +43 -0
- package/src/cli.ts +58 -6
- package/src/cycle-rules.ts +4 -5
- package/src/domain/cycle.test.ts +27 -0
- package/src/domain/cycle.ts +38 -6
- package/src/domain/paper-layout.ts +5 -2
- package/src/domain/paths.test.ts +28 -1
- package/src/domain/paths.ts +34 -1
- package/src/domain/scaffold-layout.ts +1 -1
- package/src/domain/versions-migration.test.ts +410 -0
- package/src/domain/versions-migration.ts +429 -0
- package/src/domain/versions.test.ts +564 -0
- package/src/domain/versions.ts +706 -0
- package/src/paper-settings-commands.test.ts +1 -1
- package/src/ports/move-file.ts +8 -0
- package/src/ports/yaml.ts +5 -0
- package/src/preset-card-rule.test.ts +3 -0
- package/src/presets.test.ts +55 -31
- package/src/rule-cases.test.ts +157 -8
- package/src/submission-freeze.test.ts +320 -0
- package/src/submission-new.test.ts +11 -1
- package/src/submission.ts +82 -11
- package/src/tex-venue-rules.test.ts +3 -0
- package/src/venue-rules.test.ts +2 -2
- package/src/version-rules.test.ts +222 -0
- package/src/version-rules.ts +282 -0
- package/src/versions-command.test.ts +594 -0
- package/src/versions-command.ts +360 -0
- package/src/versions.ts +286 -0
- package/dist/eslint-rules/cycle-stage.d.ts +0 -39
- package/dist/eslint-rules/cycle-stage.d.ts.map +0 -1
- package/dist/eslint-rules/cycle-stage.js +0 -119
- package/dist/eslint-rules/cycle-stage.js.map +0 -1
- package/docs/rules/cycle/stage.md +0 -85
- package/eslint-rules/cycle-stage.test.mjs +0 -131
- package/eslint-rules/cycle-stage.ts +0 -141
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/** The migration from `stages:` to folders per attempt, planned before anything moves. */
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { cycleId } from "./cycle.ts";
|
|
4
|
+
import { asPaperPath } from "./paths.ts";
|
|
5
|
+
import { err, ok } from "./result.ts";
|
|
6
|
+
import {
|
|
7
|
+
frontmatterOf,
|
|
8
|
+
planMigration,
|
|
9
|
+
rehearse,
|
|
10
|
+
withoutStages,
|
|
11
|
+
type MigrationInput,
|
|
12
|
+
} from "./versions-migration.ts";
|
|
13
|
+
import type { FileFacts } from "./versions.ts";
|
|
14
|
+
|
|
15
|
+
const SHA = "c".repeat(64);
|
|
16
|
+
const DISK: Readonly<Record<string, FileFacts>> = {
|
|
17
|
+
"versions/2026-07-22-submitted.pdf": { bytes: 305412, sha256: SHA },
|
|
18
|
+
"versions/2026-07-22-submitted.tex": { bytes: 57210, sha256: SHA },
|
|
19
|
+
"versions/2026-10-23-submitted.pdf": { bytes: 186504, sha256: SHA },
|
|
20
|
+
"versions/2026-09-01-arxiv.pdf": { bytes: 9, sha256: SHA },
|
|
21
|
+
};
|
|
22
|
+
const file = (name: string) => ({ name, kind: "file" as const });
|
|
23
|
+
|
|
24
|
+
const input = (o: Partial<MigrationInput> = {}): MigrationInput => ({
|
|
25
|
+
cycleIds: [cycleId("alpha-2026"), cycleId("beta-2027")],
|
|
26
|
+
stages: [
|
|
27
|
+
{
|
|
28
|
+
stage: "submitted",
|
|
29
|
+
cycle: "alpha-2026",
|
|
30
|
+
date: new Date("2026-07-22T00:00:00Z"),
|
|
31
|
+
pdf: "versions/2026-07-22-submitted.pdf",
|
|
32
|
+
bytes: 305412,
|
|
33
|
+
source: "versions/2026-07-22-submitted.tex",
|
|
34
|
+
sourceBytes: 57210,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
stage: "submitted",
|
|
38
|
+
cycle: "beta-2027",
|
|
39
|
+
date: "2026-10-23",
|
|
40
|
+
pdf: "versions/2026-10-23-submitted.pdf",
|
|
41
|
+
bytes: 186504,
|
|
42
|
+
sourceLost: true,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
stage: "arxiv",
|
|
46
|
+
cycle: "alpha-2026",
|
|
47
|
+
date: "2026-09-01",
|
|
48
|
+
pdf: "versions/2026-09-01-arxiv.pdf",
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
root: [
|
|
52
|
+
file("2026-07-22-submitted.pdf"),
|
|
53
|
+
file("2026-07-22-submitted.tex"),
|
|
54
|
+
file("2026-10-23-submitted.pdf"),
|
|
55
|
+
file("2026-09-01-arxiv.pdf"),
|
|
56
|
+
file(".gitkeep"),
|
|
57
|
+
],
|
|
58
|
+
factsOf: (rel) => DISK[rel] ?? null,
|
|
59
|
+
lock: {},
|
|
60
|
+
exists: () => false,
|
|
61
|
+
...o,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("planMigration", () => {
|
|
65
|
+
it("each entry's files into its cycle's folder, the lock from the declared day, a lost source recorded", () => {
|
|
66
|
+
expect(planMigration(input())).toStrictEqual(
|
|
67
|
+
ok({
|
|
68
|
+
copies: [],
|
|
69
|
+
moves: [
|
|
70
|
+
{
|
|
71
|
+
from: "versions/2026-07-22-submitted.pdf",
|
|
72
|
+
to: "versions/alpha-2026/submitted.pdf",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
from: "versions/2026-07-22-submitted.tex",
|
|
76
|
+
to: "versions/alpha-2026/submitted.tex",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
from: "versions/2026-10-23-submitted.pdf",
|
|
80
|
+
to: "versions/beta-2027/submitted.pdf",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
from: "versions/2026-09-01-arxiv.pdf",
|
|
84
|
+
to: "versions/alpha-2026/arxiv.pdf",
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
lock: {
|
|
88
|
+
"alpha-2026/submitted.pdf": {
|
|
89
|
+
kind: "file",
|
|
90
|
+
bytes: 305412,
|
|
91
|
+
sha256: SHA,
|
|
92
|
+
frozen: "2026-07-22",
|
|
93
|
+
},
|
|
94
|
+
"alpha-2026/submitted.tex": {
|
|
95
|
+
kind: "file",
|
|
96
|
+
bytes: 57210,
|
|
97
|
+
sha256: SHA,
|
|
98
|
+
frozen: "2026-07-22",
|
|
99
|
+
},
|
|
100
|
+
"beta-2027/submitted.tex": { kind: "lost", frozen: "2026-10-23" },
|
|
101
|
+
"beta-2027/submitted.pdf": {
|
|
102
|
+
kind: "file",
|
|
103
|
+
bytes: 186504,
|
|
104
|
+
sha256: SHA,
|
|
105
|
+
frozen: "2026-10-23",
|
|
106
|
+
},
|
|
107
|
+
"alpha-2026/arxiv.pdf": {
|
|
108
|
+
kind: "file",
|
|
109
|
+
bytes: 9,
|
|
110
|
+
sha256: SHA,
|
|
111
|
+
frozen: "2026-09-01",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("planMigration, beside what is there", () => {
|
|
120
|
+
it("keeps the lock already there, moves a withdrawn (STALE) file and any other loose file to extra/, leaves a file already in place and a dot-file", () => {
|
|
121
|
+
const r = planMigration(
|
|
122
|
+
input({
|
|
123
|
+
stages: [
|
|
124
|
+
{
|
|
125
|
+
stage: "submitted",
|
|
126
|
+
cycle: "beta-2027",
|
|
127
|
+
date: "2026-10-23",
|
|
128
|
+
pdf: "versions/beta-2027/submitted.pdf",
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
root: [
|
|
132
|
+
file("2026-07-20-submitted-STALE.pdf"),
|
|
133
|
+
file("lock.json"),
|
|
134
|
+
file("README.md"),
|
|
135
|
+
file(".gitkeep"),
|
|
136
|
+
],
|
|
137
|
+
factsOf: () => ({ bytes: 1, sha256: SHA }),
|
|
138
|
+
lock: { "alpha-2026/arxiv.tex": { kind: "lost", frozen: null } },
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
expect(r).toStrictEqual(
|
|
142
|
+
ok({
|
|
143
|
+
copies: [],
|
|
144
|
+
moves: [
|
|
145
|
+
{
|
|
146
|
+
from: "versions/2026-07-20-submitted-STALE.pdf",
|
|
147
|
+
to: "extra/versions/2026-07-20-submitted-STALE.pdf",
|
|
148
|
+
},
|
|
149
|
+
// versions/layout allows nothing loose beside lock.json: a note of the old layout goes too.
|
|
150
|
+
{ from: "versions/README.md", to: "extra/versions/README.md" },
|
|
151
|
+
],
|
|
152
|
+
lock: {
|
|
153
|
+
"alpha-2026/arxiv.tex": { kind: "lost", frozen: null },
|
|
154
|
+
"beta-2027/submitted.pdf": {
|
|
155
|
+
kind: "file",
|
|
156
|
+
bytes: 1,
|
|
157
|
+
sha256: SHA,
|
|
158
|
+
frozen: "2026-10-23",
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("planMigration, refused", () => {
|
|
167
|
+
it("refuses the whole migration with every reason: no place, no cycle, no date, no pdf, a file gone or replaced, one left over, two in one place", () => {
|
|
168
|
+
const r = planMigration(
|
|
169
|
+
input({
|
|
170
|
+
cycleIds: [cycleId("alpha-2026")],
|
|
171
|
+
stages: [
|
|
172
|
+
{ stage: "accepted" },
|
|
173
|
+
"not a mapping",
|
|
174
|
+
{ stage: "submitted", cycle: "gamma", date: "2026-07-22" },
|
|
175
|
+
{ stage: "submitted", cycle: "alpha-2026", date: "July" },
|
|
176
|
+
{ stage: "arxiv", cycle: "alpha-2026", date: 20260722 },
|
|
177
|
+
{ stage: "arxiv", cycle: "alpha-2026", date: "2026-09-01" },
|
|
178
|
+
{
|
|
179
|
+
stage: "submitted",
|
|
180
|
+
cycle: "alpha-2026",
|
|
181
|
+
date: "2026-07-22",
|
|
182
|
+
pdf: "versions/2026-07-22-submitted.pdf",
|
|
183
|
+
bytes: 1,
|
|
184
|
+
source: "versions/none.tex",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
stage: "submitted",
|
|
188
|
+
cycle: "alpha-2026",
|
|
189
|
+
date: "2026-10-23",
|
|
190
|
+
pdf: "versions/2026-10-23-submitted.pdf",
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
stage: "submitted",
|
|
194
|
+
cycle: "alpha-2026",
|
|
195
|
+
date: "2026-10-23",
|
|
196
|
+
pdf: "versions/2026-09-01-arxiv.pdf",
|
|
197
|
+
},
|
|
198
|
+
{ stage: ["submitted"] },
|
|
199
|
+
],
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
expect(r).toStrictEqual(
|
|
203
|
+
err([
|
|
204
|
+
"stages[0]: the stage «accepted» has no place in versions/<cycle>/ — the stages are submitted, camera-ready, arxiv",
|
|
205
|
+
"stages[1]: the stage «» has no place in versions/<cycle>/ — the stages are submitted, camera-ready, arxiv",
|
|
206
|
+
"stages[2] («submitted»): names no cycle of paperlint.json — add `cycle: <id>` (alpha-2026), the attempt the PDF was sent in, then migrate",
|
|
207
|
+
'stages[3] («submitted»): "date" is not YYYY-MM-DD',
|
|
208
|
+
'stages[4] («arxiv»): "date" is not YYYY-MM-DD',
|
|
209
|
+
'stages[5] («arxiv»): no "pdf"',
|
|
210
|
+
"stages[6]: `versions/2026-07-22-submitted.pdf` has 305412 bytes, the stage declares 1 — it is not the file that was frozen: restore it from git first",
|
|
211
|
+
"stages[6]: `versions/none.tex` is not on disk",
|
|
212
|
+
'stages[9]: the stage «["submitted"]» has no place in versions/<cycle>/ — the stages are submitted, camera-ready, arxiv',
|
|
213
|
+
"versions/2026-07-22-submitted.tex: no stage in `stages:` names it — declare it with its cycle, or move it to extra/, then migrate",
|
|
214
|
+
"versions/alpha-2026/submitted.pdf: `versions/2026-10-23-submitted.pdf` and `versions/2026-09-01-arxiv.pdf` would both go there — an attempt keeps its last upload per stage: move the older to extra/, drop its entry from `stages:`, then migrate",
|
|
215
|
+
]),
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("a paper with no cycles names none", () => {
|
|
220
|
+
const r = planMigration(input({ cycleIds: [] }));
|
|
221
|
+
expect(!r.ok && r.error[0]).toBe(
|
|
222
|
+
"stages[0] («submitted»): names no cycle of paperlint.json — add `cycle: <id>` (none declared), the attempt the PDF was sent in, then migrate",
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe("the scorecard's stages: list, taken out", () => {
|
|
228
|
+
it("the key and every line under it go; the keys around it stay", () => {
|
|
229
|
+
const md =
|
|
230
|
+
"---\ntitle: x\nstages:\n - stage: submitted\n date: 2026-07-22\n\n # a note\n- stage: arxiv\nstate: open\n---\n# Status\n";
|
|
231
|
+
expect(withoutStages(md)).toBe(
|
|
232
|
+
"---\ntitle: x\nstate: open\n---\n# Status\n",
|
|
233
|
+
);
|
|
234
|
+
expect(frontmatterOf(md)).toBe(
|
|
235
|
+
"title: x\nstages:\n - stage: submitted\n date: 2026-07-22\n\n # a note\n- stage: arxiv\nstate: open\n",
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("a frontmatter that held only the list is dropped; an inline empty list too", () => {
|
|
240
|
+
expect(withoutStages("---\nstages:\n - stage: x\n---\n# S\n")).toBe(
|
|
241
|
+
"# S\n",
|
|
242
|
+
);
|
|
243
|
+
expect(withoutStages("---\nstages: []\n---\n")).toBe("");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("no frontmatter, an empty one, or none with stages: nothing to take out", () => {
|
|
247
|
+
expect(withoutStages("# S\n")).toBeNull();
|
|
248
|
+
expect(withoutStages("---\n---\n# S\n")).toBeNull();
|
|
249
|
+
expect(withoutStages("---\ntitle: x\n---\n")).toBeNull();
|
|
250
|
+
expect(frontmatterOf("# S\n")).toBeNull();
|
|
251
|
+
expect(frontmatterOf("---\n---\n")).toBe("");
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe("planMigration, what could stop it half-way", () => {
|
|
256
|
+
it("a destination already taken is a reason, for a stage's file and for a loose one bound for extra/", () => {
|
|
257
|
+
const r = planMigration(
|
|
258
|
+
input({
|
|
259
|
+
root: [file("2026-07-22-submitted.pdf"), file("notes.md")],
|
|
260
|
+
exists: (rel) =>
|
|
261
|
+
rel === "versions/alpha-2026/submitted.pdf" ||
|
|
262
|
+
rel === "extra/versions/notes.md",
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
expect(!r.ok && r.error).toStrictEqual([
|
|
266
|
+
"versions/alpha-2026/submitted.pdf is already there, and `versions/2026-07-22-submitted.pdf` would replace it — compare the two, keep one, move the other out of versions/, then migrate",
|
|
267
|
+
"extra/versions/notes.md is already there, and `versions/notes.md` would replace it — compare the two, keep one, move the other out of versions/, then migrate",
|
|
268
|
+
]);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("one source named by two stages: copied to the first destination, moved to the last", () => {
|
|
272
|
+
const r = planMigration(
|
|
273
|
+
input({
|
|
274
|
+
stages: [
|
|
275
|
+
{
|
|
276
|
+
stage: "submitted",
|
|
277
|
+
cycle: "beta-2027",
|
|
278
|
+
date: "2026-10-23",
|
|
279
|
+
pdf: "versions/s.pdf",
|
|
280
|
+
source: "versions/s.tex",
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
stage: "arxiv",
|
|
284
|
+
cycle: "beta-2027",
|
|
285
|
+
date: "2026-10-25",
|
|
286
|
+
pdf: "versions/a.pdf",
|
|
287
|
+
source: "versions/s.tex",
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
root: [],
|
|
291
|
+
factsOf: () => ({ bytes: 1, sha256: SHA }),
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
expect(r.ok && r.value.copies).toStrictEqual([
|
|
295
|
+
{ from: "versions/s.tex", to: "versions/beta-2027/submitted.tex" },
|
|
296
|
+
]);
|
|
297
|
+
expect(r.ok && r.value.moves).toStrictEqual([
|
|
298
|
+
{ from: "versions/s.pdf", to: "versions/beta-2027/submitted.pdf" },
|
|
299
|
+
{ from: "versions/a.pdf", to: "versions/beta-2027/arxiv.pdf" },
|
|
300
|
+
{ from: "versions/s.tex", to: "versions/beta-2027/arxiv.tex" },
|
|
301
|
+
]);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe("planMigration, a path as the list spells it", () => {
|
|
306
|
+
it("a declared path is read in one spelling: `./` and `//` name the file the listing names", () => {
|
|
307
|
+
const r = planMigration(
|
|
308
|
+
input({
|
|
309
|
+
stages: [
|
|
310
|
+
{
|
|
311
|
+
stage: "submitted",
|
|
312
|
+
cycle: "beta-2027",
|
|
313
|
+
date: "2026-10-23",
|
|
314
|
+
pdf: "./versions//s.pdf",
|
|
315
|
+
source: "./versions/s.tex",
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
root: [file("s.pdf"), file("s.tex")],
|
|
319
|
+
factsOf: () => ({ bytes: 1, sha256: SHA }),
|
|
320
|
+
}),
|
|
321
|
+
);
|
|
322
|
+
expect(r.ok && r.value.moves).toStrictEqual([
|
|
323
|
+
{ from: "versions/s.pdf", to: "versions/beta-2027/submitted.pdf" },
|
|
324
|
+
{ from: "versions/s.tex", to: "versions/beta-2027/submitted.tex" },
|
|
325
|
+
]);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("a path out of the paper, as the PDF or the source, and a listed name no path can hold: reasons", () => {
|
|
329
|
+
const r = planMigration(
|
|
330
|
+
input({
|
|
331
|
+
stages: [
|
|
332
|
+
{
|
|
333
|
+
stage: "submitted",
|
|
334
|
+
cycle: "beta-2027",
|
|
335
|
+
date: "2026-10-23",
|
|
336
|
+
pdf: "../s.pdf",
|
|
337
|
+
source: "/abs/s.tex",
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
root: [file("a\\b.md")],
|
|
341
|
+
}),
|
|
342
|
+
);
|
|
343
|
+
expect(!r.ok && r.error).toStrictEqual([
|
|
344
|
+
"stages[0]: `../s.pdf` is not a path inside the paper folder — a stage names a file of this paper, relative to it",
|
|
345
|
+
"stages[0]: `/abs/s.tex` is not a path inside the paper folder — a stage names a file of this paper, relative to it",
|
|
346
|
+
"versions/a\\b.md: not a file name migrate can move — rename it, then migrate",
|
|
347
|
+
]);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
describe("rehearse", () => {
|
|
352
|
+
const p = asPaperPath;
|
|
353
|
+
const at =
|
|
354
|
+
(...there: readonly string[]) =>
|
|
355
|
+
(q: string) =>
|
|
356
|
+
there.includes(q);
|
|
357
|
+
|
|
358
|
+
it("a plan that runs to the end, each file the lock vouches for there: accepted", () => {
|
|
359
|
+
expect(
|
|
360
|
+
rehearse(
|
|
361
|
+
{
|
|
362
|
+
copies: [
|
|
363
|
+
{ from: p("versions/s.tex"), to: p("versions/c/submitted.tex") },
|
|
364
|
+
],
|
|
365
|
+
moves: [{ from: p("versions/s.tex"), to: p("versions/c/arxiv.tex") }],
|
|
366
|
+
lock: {
|
|
367
|
+
"c/submitted.tex": {
|
|
368
|
+
kind: "file",
|
|
369
|
+
bytes: 1,
|
|
370
|
+
sha256: SHA,
|
|
371
|
+
frozen: "2026-10-23",
|
|
372
|
+
},
|
|
373
|
+
"c/arxiv.pdf": { kind: "lost", frozen: null },
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
at("versions/s.tex"),
|
|
377
|
+
),
|
|
378
|
+
).toStrictEqual(ok(null));
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it("a step without its source, a step onto a file, a lock that vouches for a file not there: each refused", () => {
|
|
382
|
+
expect(
|
|
383
|
+
rehearse(
|
|
384
|
+
{
|
|
385
|
+
copies: [
|
|
386
|
+
{ from: p("versions/a.tex"), to: p("versions/c/submitted.tex") },
|
|
387
|
+
],
|
|
388
|
+
moves: [
|
|
389
|
+
{ from: p("versions/s.pdf"), to: p("versions/c/submitted.pdf") },
|
|
390
|
+
],
|
|
391
|
+
lock: {
|
|
392
|
+
"c/arxiv.pdf": {
|
|
393
|
+
kind: "file",
|
|
394
|
+
bytes: 1,
|
|
395
|
+
sha256: SHA,
|
|
396
|
+
frozen: "2026-10-23",
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
at("versions/s.pdf", "versions/c/submitted.pdf"),
|
|
401
|
+
),
|
|
402
|
+
).toStrictEqual(
|
|
403
|
+
err([
|
|
404
|
+
"copy versions/a.tex → versions/c/submitted.tex: versions/a.tex is not there at that step",
|
|
405
|
+
"move versions/s.pdf → versions/c/submitted.pdf: versions/c/submitted.pdf is already there at that step",
|
|
406
|
+
"versions/lock.json would vouch for versions/c/arxiv.pdf, which would not be there after the migration",
|
|
407
|
+
]),
|
|
408
|
+
);
|
|
409
|
+
});
|
|
410
|
+
});
|