paperlint 10.3.0 → 10.4.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 +15 -4
- package/README.md +1 -0
- package/dist/adapters/git/index.d.ts +10 -0
- package/dist/adapters/git/index.d.ts.map +1 -0
- package/dist/adapters/git/index.js +151 -0
- package/dist/adapters/git/index.js.map +1 -0
- package/dist/adapters/memory/index.d.ts +6 -0
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +8 -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/list-entries.io.d.ts +3 -0
- package/dist/adapters/node/list-entries.io.d.ts.map +1 -0
- package/dist/adapters/node/list-entries.io.js +24 -0
- package/dist/adapters/node/list-entries.io.js.map +1 -0
- package/dist/adapters/study-registry/index.d.ts +70 -10
- package/dist/adapters/study-registry/index.d.ts.map +1 -1
- package/dist/adapters/study-registry/index.js +84 -10
- package/dist/adapters/study-registry/index.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +33 -3
- package/dist/cli.js.map +1 -1
- package/dist/domain/decide-first.d.ts +100 -0
- package/dist/domain/decide-first.d.ts.map +1 -0
- package/dist/domain/decide-first.js +102 -0
- package/dist/domain/decide-first.js.map +1 -0
- package/dist/domain/index-columns.d.ts +45 -0
- package/dist/domain/index-columns.d.ts.map +1 -0
- package/dist/domain/index-columns.js +62 -0
- package/dist/domain/index-columns.js.map +1 -0
- package/dist/domain/study-mode.d.ts +37 -0
- package/dist/domain/study-mode.d.ts.map +1 -0
- package/dist/domain/study-mode.js +45 -0
- package/dist/domain/study-mode.js.map +1 -0
- package/dist/domain/study-registry.d.ts +57 -10
- package/dist/domain/study-registry.d.ts.map +1 -1
- package/dist/domain/study-registry.js +106 -30
- package/dist/domain/study-registry.js.map +1 -1
- package/dist/outputs-before-decision-rule.d.ts +17 -0
- package/dist/outputs-before-decision-rule.d.ts.map +1 -0
- package/dist/outputs-before-decision-rule.js +90 -0
- package/dist/outputs-before-decision-rule.js.map +1 -0
- package/dist/ports/git-history.d.ts +44 -0
- package/dist/ports/git-history.d.ts.map +1 -0
- package/dist/ports/git-history.js +2 -0
- package/dist/ports/git-history.js.map +1 -0
- package/dist/ports/list-entries.d.ts +17 -0
- package/dist/ports/list-entries.d.ts.map +1 -0
- package/dist/ports/list-entries.js +2 -0
- package/dist/ports/list-entries.js.map +1 -0
- package/dist/ports/study-registry-codec.d.ts +6 -3
- package/dist/ports/study-registry-codec.d.ts.map +1 -1
- package/dist/studies.d.ts +4 -0
- package/dist/studies.d.ts.map +1 -1
- package/dist/studies.js +50 -22
- package/dist/studies.js.map +1 -1
- package/dist/study-gate.d.ts +14 -0
- package/dist/study-gate.d.ts.map +1 -0
- package/dist/study-gate.js +95 -0
- package/dist/study-gate.js.map +1 -0
- package/dist/study-index-input.d.ts +5 -0
- package/dist/study-index-input.d.ts.map +1 -0
- package/dist/study-index-input.js +13 -0
- package/dist/study-index-input.js.map +1 -0
- package/dist/study-modes.d.ts +45 -0
- package/dist/study-modes.d.ts.map +1 -0
- package/dist/study-modes.js +70 -0
- package/dist/study-modes.js.map +1 -0
- package/dist/study-registry-rule.d.ts +17 -1
- package/dist/study-registry-rule.d.ts.map +1 -1
- package/dist/study-registry-rule.js +34 -13
- package/dist/study-registry-rule.js.map +1 -1
- package/dist/study-run.d.ts +19 -0
- package/dist/study-run.d.ts.map +1 -0
- package/dist/study-run.js +9 -0
- package/dist/study-run.js.map +1 -0
- package/docs/design/study-modes.md +247 -0
- package/docs/design/study-registry.md +6 -0
- package/docs/e2e.md +8 -4
- package/docs/layout.md +22 -19
- package/docs/rules/repro/outputs-before-decision.md +119 -0
- package/docs/rules/repro/study-registered.md +47 -14
- package/docs/rules.md +54 -53
- package/docs/testing.md +1 -0
- package/package.json +2 -1
- package/scripts/check.ts +6 -0
- package/skills/camera-ready/SKILL.md +6 -1
- package/skills/camera-ready/SKILL.md.spec.ts +5 -0
- package/skills/draft-paper/SKILL.md +11 -3
- package/skills/draft-paper/SKILL.md.spec.ts +10 -2
- package/skills/harden-paper/SKILL.md +12 -2
- package/skills/harden-paper/SKILL.md.spec.ts +11 -1
- package/skills/osf-artifact-upload/SKILL.md +7 -1
- package/skills/osf-artifact-upload/SKILL.md.spec.ts +6 -0
- package/skills/pc-panel-review/SKILL.md +10 -3
- package/skills/pc-panel-review/SKILL.md.spec.ts +9 -2
- package/skills/tighten-paper/SKILL.md +5 -1
- package/skills/tighten-paper/SKILL.md.spec.ts +4 -0
- package/src/adapters/git/history.test.ts +121 -0
- package/src/adapters/git/index.test.ts +268 -0
- package/src/adapters/git/index.ts +227 -0
- package/src/adapters/memory/index.test.ts +15 -1
- package/src/adapters/memory/index.ts +13 -0
- package/src/adapters/node/index.ts +1 -0
- package/src/adapters/node/list-entries.io.ts +26 -0
- package/src/adapters/node/list-entries.test.ts +43 -0
- package/src/adapters/study-registry/index.test.ts +12 -9
- package/src/adapters/study-registry/index.ts +130 -24
- package/src/adapters/study-registry/mode.test.ts +162 -0
- package/src/cli.ts +47 -3
- package/src/domain/decide-first.test.ts +258 -0
- package/src/domain/decide-first.ts +243 -0
- package/src/domain/index-columns.test.ts +216 -0
- package/src/domain/index-columns.ts +105 -0
- package/src/domain/study-measurements.test.ts +9 -5
- package/src/domain/study-mode.test.ts +55 -0
- package/src/domain/study-mode.ts +78 -0
- package/src/domain/study-registry.test.ts +38 -31
- package/src/domain/study-registry.ts +170 -31
- package/src/new-paper.test.ts +5 -1
- package/src/outputs-before-decision-rule.test.ts +206 -0
- package/src/outputs-before-decision-rule.ts +124 -0
- package/src/ports/git-history.ts +44 -0
- package/src/ports/list-entries.ts +18 -0
- package/src/ports/study-registry-codec.ts +10 -3
- package/src/rule-cases.test.ts +78 -3
- package/src/studies-index-modes.test.ts +129 -0
- package/src/studies.test.ts +35 -11
- package/src/studies.ts +88 -27
- package/src/study-gate.test.ts +316 -0
- package/src/study-gate.ts +161 -0
- package/src/study-index-input.ts +23 -0
- package/src/study-modes.ts +120 -0
- package/src/study-registry-rule.test.ts +198 -11
- package/src/study-registry-rule.ts +51 -14
- package/src/study-run.ts +21 -0
package/dist/studies.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Files } from "./ports/files.ts";
|
|
2
2
|
import type { StudyRegistryCodec } from "./ports/study-registry-codec.ts";
|
|
3
|
+
import type { ListEntries } from "./ports/list-entries.ts";
|
|
3
4
|
import type { ListDir } from "./ports/talk-media.ts";
|
|
5
|
+
import type { GitHistory, GitOrder } from "./ports/git-history.ts";
|
|
4
6
|
/** What one `studies` run was asked, after the command line. */
|
|
5
7
|
export interface StudiesArgs {
|
|
6
8
|
readonly sub: string | undefined;
|
|
@@ -13,8 +15,10 @@ export interface StudiesArgs {
|
|
|
13
15
|
readonly kind: string | null;
|
|
14
16
|
}
|
|
15
17
|
export interface StudiesDeps {
|
|
18
|
+
readonly git: GitHistory & GitOrder;
|
|
16
19
|
readonly files: Files;
|
|
17
20
|
readonly listDir: ListDir;
|
|
21
|
+
readonly entries: ListEntries;
|
|
18
22
|
readonly codec: StudyRegistryCodec;
|
|
19
23
|
readonly cwd: string;
|
|
20
24
|
/** Today, `YYYY-MM-DD`: the root reads the clock. */
|
package/dist/studies.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studies.d.ts","sourceRoot":"","sources":["../src/studies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"studies.d.ts","sourceRoot":"","sources":["../src/studies.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAIrD,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEnE,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,QAAQ,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAsPD,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAWpE"}
|
package/dist/studies.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `paperlint studies new|index <paper-dir>` — the two commands that write a paper's study registry,
|
|
3
|
-
* `repro/studies.json`, and its index in `repro/README.md` (docs/design/study-registry.md)
|
|
3
|
+
* `repro/studies.json`, and its index in `repro/README.md` (docs/design/study-registry.md) — and
|
|
4
|
+
* `studies gate <paper-dir> <id>` (src/study-gate.ts), over the studies' modes
|
|
5
|
+
* (docs/design/study-modes.md).
|
|
4
6
|
*
|
|
5
7
|
* new <paper-dir> "<title>" [--kind <kind>]
|
|
6
8
|
* the next id; `repro/<id>-<slug>/README.md` with the id and title in its frontmatter, and
|
|
@@ -16,11 +18,14 @@
|
|
|
16
18
|
import { join, relative, resolve } from "node:path";
|
|
17
19
|
import { callerPath } from "./caller-path.js";
|
|
18
20
|
import { README, REPRO_DIR } from "./domain/repro-layout.js";
|
|
19
|
-
import { DEFAULT_KIND, PLAN_FILE, STUDIES_FILE, STUDY_KINDS, addStudy,
|
|
20
|
-
import { registryOf } from "./study-registry-rule.js";
|
|
21
|
+
import { DEFAULT_KIND, PLAN_FILE, STUDIES_FILE, STUDY_KINDS, addStudy, indexCurrent, isStudyKind, planTemplate, planText, registryText, studyReadme, withIndex, } from "./domain/study-registry.js";
|
|
22
|
+
import { readableFolders, registryOf } from "./study-registry-rule.js";
|
|
23
|
+
import { runGate } from "./study-gate.js";
|
|
24
|
+
import { indexInputOf } from "./study-index-input.js";
|
|
21
25
|
const NEW_USAGE = 'npx paperlint studies new <paper-dir> "<title>" [--kind <kind>]';
|
|
22
26
|
const INDEX_USAGE = "npx paperlint studies index <paper-dir>";
|
|
23
|
-
const
|
|
27
|
+
const GATE_USAGE = "npx paperlint studies gate <paper-dir> <id>";
|
|
28
|
+
const SUBS = `new, index or gate — ${[NEW_USAGE, INDEX_USAGE, GATE_USAGE].join(" · ")}`;
|
|
24
29
|
const STATUS_FILE = "PIPELINE-STATUS.md";
|
|
25
30
|
const refuse = (code, line) => ({ code, line });
|
|
26
31
|
/** The paper folder's registry, or the refusal naming why there is none to write to. */
|
|
@@ -33,9 +38,17 @@ function registryIn(p, deps) {
|
|
|
33
38
|
case "unreadable":
|
|
34
39
|
return refuse(1, `${file} is not a study registry: ${r.why}`);
|
|
35
40
|
case "read":
|
|
36
|
-
return r.registry;
|
|
41
|
+
return escaping(p, r.registry, deps) ?? r.registry;
|
|
37
42
|
}
|
|
38
43
|
}
|
|
44
|
+
/** The refusal when a row names a folder that is not one inside `repro/`; null when none does. */
|
|
45
|
+
function escaping(p, r, deps) {
|
|
46
|
+
const readable = readableFolders(p.dir, deps);
|
|
47
|
+
const s = r.studies.find((row) => !readable(row.folder));
|
|
48
|
+
return s === undefined
|
|
49
|
+
? null
|
|
50
|
+
: refuse(1, `${s.id} in ${p.shown}/${REPRO_DIR}/${STUDIES_FILE} names the folder \`${s.folder}\`, which is not one folder inside ${REPRO_DIR}/: nothing is read or written through it. Give the row its own folder's name, then run this again.`);
|
|
51
|
+
}
|
|
39
52
|
const isRefusal = (v) => typeof v === "object" && v !== null && "code" in v && "line" in v;
|
|
40
53
|
const text = (s) => new TextEncoder().encode(s);
|
|
41
54
|
/** The README's text; null when there is none. */
|
|
@@ -48,11 +61,12 @@ const studiesCount = (r) => `${String(r.studies.length)} ${r.studies.length ===
|
|
|
48
61
|
* Write the index of `r` into the README; whether it wrote. A block already in sync is left as it
|
|
49
62
|
* is, so the padding a formatter gave the table is never undone.
|
|
50
63
|
*/
|
|
51
|
-
function writeIndex(dir, r,
|
|
52
|
-
const before = readmeOf(dir, files);
|
|
53
|
-
|
|
64
|
+
function writeIndex(dir, r, deps) {
|
|
65
|
+
const before = readmeOf(dir, deps.files);
|
|
66
|
+
const at = indexInputOf(dir, { kind: "read", registry: r }, deps);
|
|
67
|
+
if (indexCurrent(before, r, at))
|
|
54
68
|
return false;
|
|
55
|
-
files.writeAtomic(callerPath(join(dir, REPRO_DIR, README)), text(withIndex(before, r)));
|
|
69
|
+
deps.files.writeAtomic(callerPath(join(dir, REPRO_DIR, README)), text(withIndex(before, r, at)));
|
|
56
70
|
return true;
|
|
57
71
|
}
|
|
58
72
|
/** The refusal when something that is not a file sits where `repro/README.md` goes; null when none. */
|
|
@@ -76,7 +90,7 @@ function runIndex(p, a, deps) {
|
|
|
76
90
|
return blocked;
|
|
77
91
|
const readme = `${p.shown}/${REPRO_DIR}/${README}`;
|
|
78
92
|
return [
|
|
79
|
-
writeIndex(p.dir, r, deps
|
|
93
|
+
writeIndex(p.dir, r, deps)
|
|
80
94
|
? `wrote the index of ${studiesCount(r)} in ${readme}`
|
|
81
95
|
: `the index in ${readme} is in sync with ${studiesCount(r)}`,
|
|
82
96
|
];
|
|
@@ -94,13 +108,14 @@ function newArgs(a) {
|
|
|
94
108
|
: refuse(2, `--kind ${kind} is not a kind of study: one of ${STUDY_KINDS.join(", ")}`);
|
|
95
109
|
}
|
|
96
110
|
/** Write a new study's README.md and plan.json, the registry, and the index; the lines that say so. */
|
|
97
|
-
function writeStudy(p, added,
|
|
111
|
+
function writeStudy(p, added, deps) {
|
|
98
112
|
const { study, registry } = added;
|
|
113
|
+
const { files } = deps;
|
|
99
114
|
const at = (...names) => callerPath(join(p.dir, REPRO_DIR, ...names));
|
|
100
115
|
files.writeAtomic(at(study.folder, README), text(studyReadme(study)));
|
|
101
|
-
files.writeAtomic(at(study.folder, PLAN_FILE), text(planText(planTemplate())));
|
|
116
|
+
files.writeAtomic(at(study.folder, PLAN_FILE), text(planText(planTemplate(study.kind, study.folder))));
|
|
102
117
|
files.writeAtomic(at(STUDIES_FILE), text(registryText(registry)));
|
|
103
|
-
writeIndex(p.dir, registry,
|
|
118
|
+
writeIndex(p.dir, registry, deps);
|
|
104
119
|
const repro = `${p.shown}/${REPRO_DIR}`;
|
|
105
120
|
return [
|
|
106
121
|
`created ${repro}/${study.folder}/${README}`,
|
|
@@ -127,30 +142,43 @@ function runNew(p, a, deps) {
|
|
|
127
142
|
const folder = added.study.folder;
|
|
128
143
|
return deps.listDir(callerPath(join(p.dir, REPRO_DIR))).includes(folder)
|
|
129
144
|
? refuse(1, `${p.shown}/${REPRO_DIR}/${folder}/ already exists; it is never overwritten. Register it by hand, or remove it and run this again.`)
|
|
130
|
-
: writeStudy(p, added, deps
|
|
145
|
+
: writeStudy(p, added, deps);
|
|
131
146
|
}
|
|
132
147
|
/** The paper folder the command line names, or the refusal. */
|
|
133
148
|
function paperOf(a, deps) {
|
|
134
149
|
if (a.sub === undefined)
|
|
135
150
|
return refuse(2, `studies needs a subcommand: ${SUBS}`);
|
|
136
|
-
|
|
151
|
+
const sub = COMMANDS.get(a.sub);
|
|
152
|
+
if (sub === undefined)
|
|
137
153
|
return refuse(2, `unknown subcommand \`studies ${a.sub}\`: ${SUBS}`);
|
|
138
154
|
if (a.paperDir === undefined)
|
|
139
|
-
return refuse(2, `studies ${a.sub} needs the paper's folder: ${
|
|
155
|
+
return refuse(2, `studies ${a.sub} needs the paper's folder: ${sub.usage}`);
|
|
140
156
|
const dir = resolve(deps.cwd, a.paperDir);
|
|
141
157
|
const shown = relative(deps.cwd, dir) || ".";
|
|
142
158
|
return deps.files.isFile(callerPath(join(dir, STATUS_FILE)))
|
|
143
|
-
? { dir, shown }
|
|
159
|
+
? { dir, shown, run: sub.run }
|
|
144
160
|
: refuse(2, `${shown} is not a paper folder: it has no ${STATUS_FILE}`);
|
|
145
161
|
}
|
|
162
|
+
/** `gate`: the study's id, one argument, then the gate. */
|
|
163
|
+
const gateRun = (p, a, deps) => {
|
|
164
|
+
if (a.title === undefined)
|
|
165
|
+
return refuse(2, `studies gate needs the study's id: ${GATE_USAGE}`);
|
|
166
|
+
const [surplus] = a.extra;
|
|
167
|
+
if (surplus !== undefined)
|
|
168
|
+
return refuse(2, `studies gate takes the paper's folder and one id — \`${surplus}\` is one argument too many`);
|
|
169
|
+
const out = runGate(p.dir, p.shown, a.title, deps);
|
|
170
|
+
return out.ok ? out.lines : out.refusal;
|
|
171
|
+
};
|
|
172
|
+
/** Each subcommand: its usage line and its run. */
|
|
173
|
+
const COMMANDS = new Map([
|
|
174
|
+
["new", { usage: NEW_USAGE, run: runNew }],
|
|
175
|
+
["index", { usage: INDEX_USAGE, run: runIndex }],
|
|
176
|
+
["gate", { usage: GATE_USAGE, run: gateRun }],
|
|
177
|
+
]);
|
|
146
178
|
/** The run: 0 done, 1 refused by the paper's state, 2 refused by the command line. */
|
|
147
179
|
export function runStudies(a, deps) {
|
|
148
180
|
const p = paperOf(a, deps);
|
|
149
|
-
const out = isRefusal(p)
|
|
150
|
-
? p
|
|
151
|
-
: a.sub === "new"
|
|
152
|
-
? runNew(p, a, deps)
|
|
153
|
-
: runIndex(p, a, deps);
|
|
181
|
+
const out = isRefusal(p) ? p : p.run(p, a, deps);
|
|
154
182
|
if (isRefusal(out)) {
|
|
155
183
|
deps.err(out.line);
|
|
156
184
|
return out.code;
|
package/dist/studies.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studies.js","sourceRoot":"","sources":["../src/studies.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"studies.js","sourceRoot":"","sources":["../src/studies.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type StudyDeps } from "./study-modes.ts";
|
|
2
|
+
import type { Committed, GitHistory } from "./ports/git-history.ts";
|
|
3
|
+
import { type StudyRun } from "./study-run.ts";
|
|
4
|
+
export interface GateDeps extends StudyDeps {
|
|
5
|
+
readonly git: GitHistory;
|
|
6
|
+
}
|
|
7
|
+
/** What a decision's committed state means for the run. */
|
|
8
|
+
export declare const gateLine: (id: string, file: string, c: Committed) => {
|
|
9
|
+
readonly open: boolean;
|
|
10
|
+
readonly line: string;
|
|
11
|
+
};
|
|
12
|
+
/** `studies gate`: 0 when the run may start, 1 with the reason when not. */
|
|
13
|
+
export declare function runGate(paper: string, shown: string, id: string, deps: GateDeps): StudyRun;
|
|
14
|
+
//# sourceMappingURL=study-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-gate.d.ts","sourceRoot":"","sources":["../src/study-gate.ts"],"names":[],"mappings":"AAYA,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAIL,KAAK,QAAQ,EACd,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,QAAS,SAAQ,SAAS;IACzC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;CAC1B;AAoBD,2DAA2D;AAC3D,eAAO,MAAM,QAAQ,GACnB,IAAI,MAAM,EACV,MAAM,MAAM,EACZ,GAAG,SAAS,KACX;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAMA,CAAC;AAwEnD,4EAA4E;AAC5E,wBAAgB,OAAO,CACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,QAAQ,GACb,QAAQ,CAmBV"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `paperlint studies gate <paper-dir> <id>` — may the study's run start? For a study decided before
|
|
3
|
+
* its run (the default mode), only once its decision file exists and is committed as it stands: exit 0
|
|
4
|
+
* with the commit and its date, else exit 1 saying why. A runner calls it first:
|
|
5
|
+
*
|
|
6
|
+
* npx paperlint studies gate papers/my-paper P1-S03 && ./run.sh
|
|
7
|
+
*
|
|
8
|
+
* An exploratory study has no gate: it exits 0 and says so. A study that declares no mode, names no
|
|
9
|
+
* decision file, or whose row names a folder that is not one inside `repro/`, is refused.
|
|
10
|
+
*/
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { callerPath } from "./caller-path.js";
|
|
13
|
+
import { modeStudies, onDisk, } from "./study-modes.js";
|
|
14
|
+
import { REPRO_DIR } from "./domain/repro-layout.js";
|
|
15
|
+
import { decisionAsOutput } from "./domain/study-mode.js";
|
|
16
|
+
import { readableFolders, registryOf } from "./study-registry-rule.js";
|
|
17
|
+
import { refusal, refused, } from "./study-run.js";
|
|
18
|
+
/** The line of a committed state that holds the run back. */
|
|
19
|
+
const shutLine = (id, file, c) => {
|
|
20
|
+
switch (c.kind) {
|
|
21
|
+
case "changed":
|
|
22
|
+
return `${id}: the decision ${file} has changed since its last commit, ${c.commit}; the run waits for the decision as committed. Commit it, then run this again.`;
|
|
23
|
+
case "deleted":
|
|
24
|
+
return `${id}: the decision ${file} is not in HEAD: it was deleted in ${c.commit}; the run waits for a decision that stands. Restore it, commit it, then run this again.`;
|
|
25
|
+
case "not-committed":
|
|
26
|
+
return `${id}: the decision ${file} is not committed; the run waits for it. Write the decision there, commit it, then run this again.`;
|
|
27
|
+
case "unknown":
|
|
28
|
+
return `${id}: git could not say whether the decision ${file} is committed: ${c.why}.`;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/** What a decision's committed state means for the run. */
|
|
32
|
+
export const gateLine = (id, file, c) => c.kind === "committed"
|
|
33
|
+
? {
|
|
34
|
+
open: true,
|
|
35
|
+
line: `${id}: the decision ${file} is committed in ${c.commit} (${c.date}); the run may start.`,
|
|
36
|
+
}
|
|
37
|
+
: { open: false, line: shutLine(id, file, c) };
|
|
38
|
+
/**
|
|
39
|
+
* Why the decision cannot gate the run though it is named: its own outputs match it, or it is a
|
|
40
|
+
* directory (git answers for a directory with the last commit of anything under it); null when usable.
|
|
41
|
+
*/
|
|
42
|
+
const unusable = (paper, { id, folder }, mode, deps) => {
|
|
43
|
+
const file = mode.decision.file;
|
|
44
|
+
const glob = decisionAsOutput(folder, mode);
|
|
45
|
+
if (glob !== null)
|
|
46
|
+
return refusal(`${id}: its decision ${file} is matched by its output \`${glob}\`; a decision cannot be what its own run produces.`);
|
|
47
|
+
const on = onDisk(join(paper, file), deps);
|
|
48
|
+
return on === "directory" || on === "symbolic link"
|
|
49
|
+
? refusal(`${id}: its decision ${file} is a ${on}, not a file; the run cannot be gated on it. Name the decision file in \`mode.decision.file\`.`)
|
|
50
|
+
: null;
|
|
51
|
+
};
|
|
52
|
+
/** The gate of a decide-first study. */
|
|
53
|
+
const gateDecided = (paper, { id, folder }, mode, deps) => {
|
|
54
|
+
const file = mode.decision.file;
|
|
55
|
+
const blocked = unusable(paper, { id, folder }, mode, deps);
|
|
56
|
+
if (blocked !== null)
|
|
57
|
+
return refused(blocked);
|
|
58
|
+
const g = gateLine(id, file, deps.git.committed(callerPath(paper), callerPath(join(paper, file))));
|
|
59
|
+
return g.open ? { ok: true, lines: [g.line] } : refused(refusal(g.line));
|
|
60
|
+
};
|
|
61
|
+
/** The refusal when the row of `id` names a folder that is not one inside `repro/`; null when none does. */
|
|
62
|
+
const escapingRow = (paper, shown, id, deps) => {
|
|
63
|
+
const r = registryOf(paper, deps);
|
|
64
|
+
const readable = readableFolders(paper, deps);
|
|
65
|
+
const row = (r.kind === "read" ? r.registry.studies : []).find((x) => x.id === id && !readable(x.folder));
|
|
66
|
+
return row === undefined
|
|
67
|
+
? null
|
|
68
|
+
: refusal(`${id}: its row names the folder \`${row.folder}\`, which is not one folder inside ${shown}/${REPRO_DIR}/; nothing is read through it. Give the row its own folder's name.`);
|
|
69
|
+
};
|
|
70
|
+
/** The refusal of a study whose plan is still a draft: a mode decides nothing until its plan is ready. */
|
|
71
|
+
const draftRefusal = (id, s) => {
|
|
72
|
+
const names = s.empty.map((f) => `\`${f}\``).join(", ");
|
|
73
|
+
const verb = s.empty.length === 1 ? "is" : "are";
|
|
74
|
+
return refusal(`${id}: its ${s.plan} is a draft — ${names} ${verb} empty; the run waits for a ready plan. Fill it in, then run this again.`);
|
|
75
|
+
};
|
|
76
|
+
/** `studies gate`: 0 when the run may start, 1 with the reason when not. */
|
|
77
|
+
export function runGate(paper, shown, id, deps) {
|
|
78
|
+
const escaping = escapingRow(paper, shown, id, deps);
|
|
79
|
+
if (escaping !== null)
|
|
80
|
+
return refused(escaping);
|
|
81
|
+
const s = modeStudies(paper, deps).find((m) => m.row.id === id);
|
|
82
|
+
if (s === undefined)
|
|
83
|
+
return refused(refusal(`${id} declares no mode in ${shown}: no row of ${shown}/${REPRO_DIR}/studies.json with this id names a plan.json that parses with \`mode\`. Lint the paper (\`npx paperlint lint\`) to see why.`));
|
|
84
|
+
if (s.empty.length > 0)
|
|
85
|
+
return refused(draftRefusal(id, s));
|
|
86
|
+
return s.mode.kind === "decide_before_run"
|
|
87
|
+
? gateDecided(paper, s.row, s.mode, deps)
|
|
88
|
+
: {
|
|
89
|
+
ok: true,
|
|
90
|
+
lines: [
|
|
91
|
+
`${id} is exploratory (${s.mode.reason}): it has no gate, and its results are reported as exploratory.`,
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=study-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-gate.js","sourceRoot":"","sources":["../src/study-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IndexInput, RegistryState } from "./domain/study-registry.ts";
|
|
2
|
+
import { type OrderDeps } from "./outputs-before-decision-rule.ts";
|
|
3
|
+
/** The plans and the histories of the paper folder `paper`, whose registry is `r`. */
|
|
4
|
+
export declare const indexInputOf: (paper: string, r: RegistryState, deps: OrderDeps) => IndexInput;
|
|
5
|
+
//# sourceMappingURL=study-index-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-index-input.d.ts","sourceRoot":"","sources":["../src/study-index-input.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,mCAAmC,CAAC;AAG3C,sFAAsF;AACtF,eAAO,MAAM,YAAY,GACvB,OAAO,MAAM,EACb,GAAG,aAAa,EAChB,MAAM,SAAS,KACd,UAKD,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `paperlint studies index` and `studies new` generate the index from beyond the registry: each
|
|
3
|
+
* row's plan, and each decide-first study's history as git gives it now (src/domain/index-columns.ts).
|
|
4
|
+
*/
|
|
5
|
+
import { indexHistory } from "./domain/index-columns.js";
|
|
6
|
+
import { decideFirstStudies, } from "./outputs-before-decision-rule.js";
|
|
7
|
+
import { plansOf, readableFolders } from "./study-registry-rule.js";
|
|
8
|
+
/** The plans and the histories of the paper folder `paper`, whose registry is `r`. */
|
|
9
|
+
export const indexInputOf = (paper, r, deps) => ({
|
|
10
|
+
plans: plansOf(paper, r, deps, readableFolders(paper, deps)),
|
|
11
|
+
history: new Map(decideFirstStudies(paper, deps).map((s) => [s.id, indexHistory(s)])),
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=study-index-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-index-input.js","sourceRoot":"","sources":["../src/study-index-input.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,OAAO,EACL,kBAAkB,GAEnB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEpE,sFAAsF;AACtF,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,KAAa,EACb,CAAgB,EAChB,IAAe,EACH,EAAE,CAAC,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC5D,OAAO,EAAE,IAAI,GAAG,CACd,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CACpE;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { StudyMode } from "./domain/study-mode.ts";
|
|
2
|
+
import type { NotAFile } from "./domain/decide-first.ts";
|
|
3
|
+
import { type PlanTextField, type Study } from "./domain/study-registry.ts";
|
|
4
|
+
import type { Files } from "./ports/files.ts";
|
|
5
|
+
import type { StudyRegistryCodec } from "./ports/study-registry-codec.ts";
|
|
6
|
+
import type { ListEntries } from "./ports/list-entries.ts";
|
|
7
|
+
import type { ListDir } from "./ports/talk-media.ts";
|
|
8
|
+
/** What reading a paper's studies needs: the disk, its listings with and without kinds, and the parsers. */
|
|
9
|
+
export interface StudyDeps {
|
|
10
|
+
readonly files: Files;
|
|
11
|
+
readonly listDir: ListDir;
|
|
12
|
+
readonly entries: ListEntries;
|
|
13
|
+
readonly codec: StudyRegistryCodec;
|
|
14
|
+
}
|
|
15
|
+
/** What a decision path names on disk: a file, nothing, or something git cannot vouch for. */
|
|
16
|
+
export type OnDisk = "file" | "absent" | NotAFile;
|
|
17
|
+
/**
|
|
18
|
+
* What `p` is on disk, read from its directory's entries without following a link: a decision must
|
|
19
|
+
* be a regular file — git answers for a directory with the commits of anything under it, and for a
|
|
20
|
+
* link with the link, not the file it points to.
|
|
21
|
+
*/
|
|
22
|
+
export declare const onDisk: (p: string, deps: Pick<StudyDeps, "entries">) => OnDisk;
|
|
23
|
+
/**
|
|
24
|
+
* Every file under `dir`, relative to it, `/`-separated; none when it is not a directory. A symbolic
|
|
25
|
+
* link is listed as a file — git stores it as one — and never followed: a link to the folder itself
|
|
26
|
+
* would recurse forever, and one to a tree outside would bring its files into the study.
|
|
27
|
+
*/
|
|
28
|
+
export declare const filesUnder: (dir: string, deps: Pick<StudyDeps, "entries">, prefix?: string) => readonly string[];
|
|
29
|
+
/**
|
|
30
|
+
* A study whose plan parsed with a mode: its row, the plan's path in its folder, the mode, and the
|
|
31
|
+
* plan's empty texts — none when it is ready. Only a ready plan's mode decides anything: a draft may
|
|
32
|
+
* name no outputs, or an empty decision file.
|
|
33
|
+
*/
|
|
34
|
+
export interface ModeStudy {
|
|
35
|
+
readonly row: Study;
|
|
36
|
+
readonly plan: string;
|
|
37
|
+
readonly mode: StudyMode;
|
|
38
|
+
readonly empty: readonly PlanTextField[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Every study of the paper folder `paper` whose plan declares a mode, in registry order. A plan that
|
|
42
|
+
* does not parse is `repro/study-registered`'s to report; here it declares nothing.
|
|
43
|
+
*/
|
|
44
|
+
export declare const modeStudies: (paper: string, deps: Pick<StudyDeps, "files" | "codec" | "entries">) => readonly ModeStudy[];
|
|
45
|
+
//# sourceMappingURL=study-modes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-modes.d.ts","sourceRoot":"","sources":["../src/study-modes.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,KAAK,EACX,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,KAAK,EAAY,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,4GAA4G;AAC5G,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;CACpC;AAED,8FAA8F;AAC9F,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAQlD;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,GAAG,MAAM,EAAE,MAAM,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,KAAG,MAKpE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GACrB,KAAK,MAAM,EACX,MAAM,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAChC,eAAW,KACV,SAAS,MAAM,EAId,CAAC;AAEL;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;CAC1C;AA6BD;;;GAGG;AACH,eAAO,MAAM,WAAW,GACtB,OAAO,MAAM,EACb,MAAM,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC,KACnD,SAAS,SAAS,EAOpB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a paper's studies with their modes: each registered study whose `plan.json` parses with a
|
|
3
|
+
* `mode`, and the files under a study's folder. What `repro/outputs-before-decision`, `studies gate`
|
|
4
|
+
* and the index's mode columns read with (docs/design/study-modes.md). A row whose folder is not one
|
|
5
|
+
* folder inside `repro/` is never read through.
|
|
6
|
+
*/
|
|
7
|
+
import { basename, dirname, join } from "node:path";
|
|
8
|
+
import { callerPath } from "./caller-path.js";
|
|
9
|
+
import { REPRO_DIR } from "./domain/repro-layout.js";
|
|
10
|
+
import { isInside, } from "./domain/study-registry.js";
|
|
11
|
+
import { readableFolders, registryOf, textAt } from "./study-registry-rule.js";
|
|
12
|
+
const ON_DISK = {
|
|
13
|
+
file: "file",
|
|
14
|
+
dir: "directory",
|
|
15
|
+
symlink: "symbolic link",
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* What `p` is on disk, read from its directory's entries without following a link: a decision must
|
|
19
|
+
* be a regular file — git answers for a directory with the commits of anything under it, and for a
|
|
20
|
+
* link with the link, not the file it points to.
|
|
21
|
+
*/
|
|
22
|
+
export const onDisk = (p, deps) => {
|
|
23
|
+
const e = deps
|
|
24
|
+
.entries(callerPath(dirname(p)))
|
|
25
|
+
.find((x) => x.name === basename(p));
|
|
26
|
+
return e === undefined ? "absent" : ON_DISK[e.kind];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Every file under `dir`, relative to it, `/`-separated; none when it is not a directory. A symbolic
|
|
30
|
+
* link is listed as a file — git stores it as one — and never followed: a link to the folder itself
|
|
31
|
+
* would recurse forever, and one to a tree outside would bring its files into the study.
|
|
32
|
+
*/
|
|
33
|
+
export const filesUnder = (dir, deps, prefix = "") => deps.entries(callerPath(dir)).flatMap(({ name, kind }) => {
|
|
34
|
+
const rel = prefix === "" ? name : `${prefix}/${name}`;
|
|
35
|
+
return kind === "dir" ? filesUnder(join(dir, name), deps, rel) : [rel];
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* The study `s`'s plan, when it is a plan inside a folder every reader may read through
|
|
39
|
+
* (`readableFolders`), by the registry rule's own containment test (`isInside`), that parsed with a
|
|
40
|
+
* mode; else null.
|
|
41
|
+
*/
|
|
42
|
+
const modeStudyOf = (paper, s, deps, readable) => {
|
|
43
|
+
const plan = s.plan;
|
|
44
|
+
const text = plan === undefined ||
|
|
45
|
+
!readable(s.folder) ||
|
|
46
|
+
!isInside(plan) ||
|
|
47
|
+
plan.includes("\0")
|
|
48
|
+
? null
|
|
49
|
+
: textAt(deps.files, join(paper, REPRO_DIR, s.folder, plan));
|
|
50
|
+
if (plan === undefined || text === null)
|
|
51
|
+
return null;
|
|
52
|
+
const parsed = deps.codec.parsePlan(text, s.kind);
|
|
53
|
+
if (parsed.kind === "unreadable" || parsed.plan.mode === undefined)
|
|
54
|
+
return null;
|
|
55
|
+
const empty = parsed.kind === "draft" ? parsed.empty : [];
|
|
56
|
+
return { row: s, plan, mode: parsed.plan.mode, empty };
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Every study of the paper folder `paper` whose plan declares a mode, in registry order. A plan that
|
|
60
|
+
* does not parse is `repro/study-registered`'s to report; here it declares nothing.
|
|
61
|
+
*/
|
|
62
|
+
export const modeStudies = (paper, deps) => {
|
|
63
|
+
const r = registryOf(paper, deps);
|
|
64
|
+
const readable = readableFolders(paper, deps);
|
|
65
|
+
return (r.kind === "read" ? r.registry.studies : []).flatMap((s) => {
|
|
66
|
+
const m = modeStudyOf(paper, s, deps, readable);
|
|
67
|
+
return m === null ? [] : [m];
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=study-modes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-modes.js","sourceRoot":"","sources":["../src/study-modes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,29 @@
|
|
|
1
|
-
import { type RegistryState, type StudyFinding } from "./domain/study-registry.ts";
|
|
1
|
+
import { type PlanState, type RegistryState, type StudyFinding } from "./domain/study-registry.ts";
|
|
2
2
|
import type { FolderLayoutDeps } from "./folder-layout-rule.ts";
|
|
3
3
|
import type { FolderRuleModule } from "./folder-venue-rule.ts";
|
|
4
|
+
import type { Files } from "./ports/files.ts";
|
|
5
|
+
import type { ListEntries } from "./ports/list-entries.ts";
|
|
4
6
|
import type { StudyRegistryCodec } from "./ports/study-registry-codec.ts";
|
|
5
7
|
/** What the rule reads with: the disk, its listings, and the registry's parser. */
|
|
6
8
|
export interface StudyRegistryDeps extends FolderLayoutDeps {
|
|
7
9
|
readonly codec: StudyRegistryCodec;
|
|
10
|
+
/** `repro/`'s entries, links not followed: a study folder that is a link is never read through. */
|
|
11
|
+
readonly entries: ListEntries;
|
|
8
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* The test every reader of a study's folder passes a row through: its folder is one folder name
|
|
15
|
+
* inside `repro/` (`isFolderName`) and not a symbolic link, which would lead the reads anywhere.
|
|
16
|
+
*/
|
|
17
|
+
export declare const readableFolders: (dir: string, deps: Pick<StudyRegistryDeps, "entries">) => ((folder: string) => boolean);
|
|
18
|
+
/** The text of the file at `p`; null when there is none, or a directory sits there. */
|
|
19
|
+
export declare const textAt: (files: Files, p: string) => string | null;
|
|
9
20
|
/** The registry of the paper folder `dir`: absent, unreadable with why, or read. */
|
|
10
21
|
export declare function registryOf(dir: string, deps: Pick<StudyRegistryDeps, "files" | "codec">): RegistryState;
|
|
22
|
+
/**
|
|
23
|
+
* The plan each row of `r` names, read and parsed, by `<folder>/<plan>`; a plan not there is left out.
|
|
24
|
+
* A plan still named `prereg.json` is parsed by the loose schema of its old name.
|
|
25
|
+
*/
|
|
26
|
+
export declare const plansOf: (dir: string, r: RegistryState, deps: Pick<StudyRegistryDeps, "files" | "codec">, readable: (folder: string) => boolean) => ReadonlyMap<string, PlanState>;
|
|
11
27
|
/** Every finding of the paper folder `dir`'s study registry — THE function behind both rules. */
|
|
12
28
|
export declare function studyFindings(dir: string, deps: StudyRegistryDeps): readonly StudyFinding[];
|
|
13
29
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"study-registry-rule.d.ts","sourceRoot":"","sources":["../src/study-registry-rule.ts"],"names":[],"mappings":"AA2BA,OAAO,EAQL,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"study-registry-rule.d.ts","sourceRoot":"","sources":["../src/study-registry-rule.ts"],"names":[],"mappings":"AA2BA,OAAO,EAQL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAM1E,mFAAmF;AACnF,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,mGAAmG;IACnG,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B;AAcD;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK,MAAM,EACX,MAAM,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,KACvC,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAG9B,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,EAAE,GAAG,MAAM,KAAG,MAAM,GAAG,IAGzD,CAAC;AAEF,oFAAoF;AACpF,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAAC,GAC/C,aAAa,CAOf;AAmCD;;;GAGG;AACH,eAAO,MAAM,OAAO,GAClB,KAAK,MAAM,EACX,GAAG,aAAa,EAChB,MAAM,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAAC,EAChD,UAAU,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,KACpC,WAAW,CAAC,MAAM,EAAE,SAAS,CAkB7B,CAAC;AAEJ,iGAAiG;AACjG,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,iBAAiB,GACtB,SAAS,YAAY,EAAE,CAUzB;AA+DD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,iBAAiB,GACtB,QAAQ,CAAC,MAAM,CAAC,kBAAkB,GAAG,iBAAiB,EAAE,gBAAgB,CAAC,CAAC,CAqB5E;AAED,mGAAmG;AACnG,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC;IAChD,wBAAwB,EAAE,OAAO,CAAC;IAClC,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAA0E,CAAC"}
|
|
@@ -20,12 +20,25 @@
|
|
|
20
20
|
import { basename, dirname, join, posix } from "node:path";
|
|
21
21
|
import { callerPath } from "./caller-path.js";
|
|
22
22
|
import { README, REPRO_BYPRODUCTS, REPRO_DIR, REPRO_DIRS, } from "./domain/repro-layout.js";
|
|
23
|
-
import { LEGACY_PLAN_FILE, PLAN_NAME_PROBLEMS, STUDIES_FILE, judgeStudies,
|
|
23
|
+
import { LEGACY_PLAN_FILE, PLAN_NAME_PROBLEMS, STUDIES_FILE, isFolderName, judgeStudies, planRefs, studyMessage, } from "./domain/study-registry.js";
|
|
24
24
|
import { reproEntriesOf } from "./repro-layout-rule.js";
|
|
25
25
|
import { rulePageUrl } from "./venue-rules.js";
|
|
26
26
|
const STATUS_FILE = "PIPELINE-STATUS.md";
|
|
27
|
+
/** The names at the top of `<dir>/repro/` that are symbolic links. */
|
|
28
|
+
const linksIn = (dir, deps) => new Set(deps
|
|
29
|
+
.entries(callerPath(join(dir, REPRO_DIR)))
|
|
30
|
+
.filter((e) => e.kind === "symlink")
|
|
31
|
+
.map((e) => e.name));
|
|
32
|
+
/**
|
|
33
|
+
* The test every reader of a study's folder passes a row through: its folder is one folder name
|
|
34
|
+
* inside `repro/` (`isFolderName`) and not a symbolic link, which would lead the reads anywhere.
|
|
35
|
+
*/
|
|
36
|
+
export const readableFolders = (dir, deps) => {
|
|
37
|
+
const links = linksIn(dir, deps);
|
|
38
|
+
return (folder) => isFolderName(folder) && !links.has(folder);
|
|
39
|
+
};
|
|
27
40
|
/** The text of the file at `p`; null when there is none, or a directory sits there. */
|
|
28
|
-
const textAt = (files, p) => {
|
|
41
|
+
export const textAt = (files, p) => {
|
|
29
42
|
const b = files.isFile(callerPath(p)) ? files.readBytes(callerPath(p)) : null;
|
|
30
43
|
return b === null ? null : new TextDecoder().decode(b);
|
|
31
44
|
};
|
|
@@ -46,7 +59,9 @@ const studyFolders = (dir, deps) => reproEntriesOf(dir, deps)
|
|
|
46
59
|
!REPRO_BYPRODUCTS.includes(e.name))
|
|
47
60
|
.map((e) => e.name);
|
|
48
61
|
/** Each `<folder>/<result_file>` of `r` that is a file. */
|
|
49
|
-
const existingResults = (dir, r, files) => new Set((r.kind === "read" ? r.registry.studies : [])
|
|
62
|
+
const existingResults = (dir, r, files, readable) => new Set((r.kind === "read" ? r.registry.studies : [])
|
|
63
|
+
// A row whose folder is not one inside `repro/`, or is a link, is never read through.
|
|
64
|
+
.filter((s) => readable(s.folder))
|
|
50
65
|
.flatMap((s) => s.result_file === undefined ? [] : [`${s.folder}/${s.result_file}`])
|
|
51
66
|
// A NUL is no path the disk can hold, and probing one throws: such a row reads as missing.
|
|
52
67
|
.filter((p) => !p.includes("\0") &&
|
|
@@ -55,27 +70,33 @@ const existingResults = (dir, r, files) => new Set((r.kind === "read" ? r.regist
|
|
|
55
70
|
* The plan each row of `r` names, read and parsed, by `<folder>/<plan>`; a plan not there is left out.
|
|
56
71
|
* A plan still named `prereg.json` is parsed by the loose schema of its old name.
|
|
57
72
|
*/
|
|
58
|
-
const plansOf = (dir, r, deps) => new Map((r.kind === "read" ?
|
|
59
|
-
// A NUL is no path the disk can hold, and probing one throws: such a plan reads as missing.
|
|
60
|
-
|
|
61
|
-
.
|
|
73
|
+
export const plansOf = (dir, r, deps, readable) => new Map((r.kind === "read" ? planRefs(r.registry) : [])
|
|
74
|
+
// A NUL is no path the disk can hold, and probing one throws: such a plan reads as missing. A
|
|
75
|
+
// folder that is a link is never read through.
|
|
76
|
+
.filter(({ key, folder }) => !key.includes("\0") && readable(folder))
|
|
77
|
+
.flatMap(({ key, kind }) => {
|
|
62
78
|
const text = textAt(deps.files, join(dir, REPRO_DIR, key));
|
|
63
79
|
if (text === null)
|
|
64
80
|
return [];
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
return [
|
|
82
|
+
[
|
|
83
|
+
key,
|
|
84
|
+
posix.basename(key) === LEGACY_PLAN_FILE
|
|
85
|
+
? deps.codec.parseLegacyPlan(text)
|
|
86
|
+
: deps.codec.parsePlan(text, kind),
|
|
87
|
+
],
|
|
88
|
+
];
|
|
69
89
|
}));
|
|
70
90
|
/** Every finding of the paper folder `dir`'s study registry — THE function behind both rules. */
|
|
71
91
|
export function studyFindings(dir, deps) {
|
|
92
|
+
const readable = readableFolders(dir, deps);
|
|
72
93
|
const registry = registryOf(dir, deps);
|
|
73
94
|
return judgeStudies({
|
|
74
95
|
folders: studyFolders(dir, deps),
|
|
75
96
|
registry,
|
|
76
97
|
readme: textAt(deps.files, join(dir, REPRO_DIR, README)),
|
|
77
|
-
existingResults: existingResults(dir, registry, deps.files),
|
|
78
|
-
plans: plansOf(dir, registry, deps),
|
|
98
|
+
existingResults: existingResults(dir, registry, deps.files, readable),
|
|
99
|
+
plans: plansOf(dir, registry, deps, readable),
|
|
79
100
|
});
|
|
80
101
|
}
|
|
81
102
|
/** The problems `repro/study-registered` reports, each its own message id; the text is `studyMessage`. */
|