spec-controller 0.1.0-alpha.36 → 0.1.0-alpha.38
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/README.md +7 -3
- package/dist/cli-allocate/cli.d.ts.map +1 -1
- package/dist/cli-allocate/cli.js +17 -5
- package/dist/cli-allocate/cli.js.map +1 -1
- package/dist/cli-allocate/readHistory.d.ts +10 -2
- package/dist/cli-allocate/readHistory.d.ts.map +1 -1
- package/dist/cli-allocate/readHistory.js +45 -6
- package/dist/cli-allocate/readHistory.js.map +1 -1
- package/dist/cli-allocate/readParts.d.ts.map +1 -1
- package/dist/cli-allocate/readParts.js +5 -2
- package/dist/cli-allocate/readParts.js.map +1 -1
- package/dist/cli-allocate/readTree.d.ts +4 -5
- package/dist/cli-allocate/readTree.d.ts.map +1 -1
- package/dist/cli-allocate/readTree.js +75 -32
- package/dist/cli-allocate/readTree.js.map +1 -1
- package/dist/cli-allocate/refStore.d.ts +3 -0
- package/dist/cli-allocate/refStore.d.ts.map +1 -0
- package/dist/cli-allocate/refStore.js +47 -0
- package/dist/cli-allocate/refStore.js.map +1 -0
- package/dist/cli-allocate/unreadable.d.ts +24 -0
- package/dist/cli-allocate/unreadable.d.ts.map +1 -0
- package/dist/cli-allocate/unreadable.js +43 -0
- package/dist/cli-allocate/unreadable.js.map +1 -0
- package/dist/cli-balance/cli.d.ts +3 -2
- package/dist/cli-balance/cli.d.ts.map +1 -1
- package/dist/cli-balance/cli.js +36 -13
- package/dist/cli-balance/cli.js.map +1 -1
- package/dist/cli-balance/unreadableInputs.d.ts +35 -0
- package/dist/cli-balance/unreadableInputs.d.ts.map +1 -0
- package/dist/cli-balance/unreadableInputs.js +101 -0
- package/dist/cli-balance/unreadableInputs.js.map +1 -0
- package/dist/ingest/ingestQualityChecks.d.ts +25 -8
- package/dist/ingest/ingestQualityChecks.d.ts.map +1 -1
- package/dist/ingest/ingestQualityChecks.js +93 -33
- package/dist/ingest/ingestQualityChecks.js.map +1 -1
- package/dist/ingest/ingestScenarios.d.ts +4 -4
- package/dist/ingest/ingestScenarios.d.ts.map +1 -1
- package/dist/ingest/ingestScenarios.js +21 -8
- package/dist/ingest/ingestScenarios.js.map +1 -1
- package/dist/ingest/inputShapes.d.ts +47 -0
- package/dist/ingest/inputShapes.d.ts.map +1 -0
- package/dist/ingest/inputShapes.js +143 -0
- package/dist/ingest/inputShapes.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ A scenario tagged `@unit` names an obligation — a unit test must exist and pas
|
|
|
70
70
|
|
|
71
71
|
The whole report is written before the code is set, so a failing run still tells you why.
|
|
72
72
|
|
|
73
|
-
`spec-controller allocate`, which prints the next scenario code for a feature part, reaches no verdict: it exits `0` when it answers and `2` for a usage error or an unknown part.
|
|
73
|
+
`spec-controller allocate`, which prints the next scenario code for a feature part, reaches no verdict: it exits `0` when it answers and `2` for a usage error or an unknown part. A file or directory it cannot read, whether it reads it itself or through git, is a usage error: it is refused, naming the path and the reason, and no code is given.
|
|
74
74
|
|
|
75
75
|
`--exit-zero` moves `1` and `3` to `0` and leaves every other code alone: see *Report first, gate later*.
|
|
76
76
|
|
|
@@ -79,8 +79,12 @@ The whole report is written before the code is set, so a failing run still tells
|
|
|
79
79
|
**Node's default is never one of these.** A run that cannot reconcile ends on an enumerated code of
|
|
80
80
|
its own rather than falling out of the process — a crash that exits `1` would be indistinguishable,
|
|
81
81
|
by status, from an honest disagreement. **An input the tool cannot read is part of `2`**: a mistyped
|
|
82
|
-
path,
|
|
83
|
-
|
|
82
|
+
path, a path it is not permitted to read, a file where a directory belongs or the reverse, an
|
|
83
|
+
unparseable report, a report that parses but is not in the shape its runner writes, a saved
|
|
84
|
+
reconciliation it cannot render, or a corpus the Gherkin parser rejects are all the invocation
|
|
85
|
+
being wrong, never a verdict about your code. The refusal names the input, the path, and what was
|
|
86
|
+
wrong with it. A well-formed report that holds nothing, such as a Vitest run that matched no test
|
|
87
|
+
file, is read, not refused. **So is an output location it cannot write**: a `--format` path that is
|
|
84
88
|
read-only, a directory, under a regular file or in a directory that does not exist, and a
|
|
85
89
|
`--store` root the run directory cannot be created under, are refused at `2`, naming the location and the operating system's reason, before any report is printed.
|
|
86
90
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/cli.ts"],"names":[],"mappings":"AAcA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAiBhD"}
|
package/dist/cli-allocate/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { cliRegistry, renderCommandHelp } from "../cli-registry.js";
|
|
|
7
7
|
import { resolveToolIdentity } from "../run-management/resolveRunInputs.js";
|
|
8
8
|
import { readHeaderCodes } from "./readParts.js";
|
|
9
9
|
import { readRequest } from "./request.js";
|
|
10
|
+
import { readOrRefuse, refusalLine, UnreadableInput } from "./unreadable.js";
|
|
10
11
|
import { historyBoundaryOf, readHistory, repositoryTop } from "./readHistory.js";
|
|
11
12
|
import { readTree, walkTree } from "./readTree.js";
|
|
12
13
|
import { alreadyHeadedNote, highestSeenLine, historyBoundaryLine, unknownPartRefusal } from "./stderr.js";
|
|
@@ -20,11 +21,22 @@ export function runAllocate(argv) {
|
|
|
20
21
|
process.stderr.write(request.usageError + "\n");
|
|
21
22
|
process.exit(2);
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
try {
|
|
25
|
+
answerRequest(request);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (!(err instanceof UnreadableInput))
|
|
29
|
+
throw err;
|
|
30
|
+
process.stderr.write(refusalLine(err) + "\n");
|
|
31
|
+
process.exit(2);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Read the tree, its history and its parts, and write the answer to a well-formed request, or its refusal. */
|
|
35
|
+
function answerRequest({ part, isNew }) {
|
|
36
|
+
const cwd = readOrRefuse("the working directory", "", () => process.cwd());
|
|
25
37
|
const top = repositoryTop(cwd);
|
|
26
38
|
const headerCodes = readHeaderCodes(join(top ?? cwd, "features"));
|
|
27
|
-
const answer = nextScnCode({ part, headerCodes, isNew }, sightingsFrom(cwd, part, top
|
|
39
|
+
const answer = nextScnCode({ part, headerCodes, isNew }, sightingsFrom(cwd, part, top));
|
|
28
40
|
if (answer.kind === "unknown-part") {
|
|
29
41
|
process.stderr.write(unknownPartRefusal(answer) + "\n");
|
|
30
42
|
process.exit(2);
|
|
@@ -39,7 +51,7 @@ export function runAllocate(argv) {
|
|
|
39
51
|
process.stdout.write(answer.code + "\n");
|
|
40
52
|
}
|
|
41
53
|
/** The tree and its history through git, or the tree alone by a walk where there is no repository. */
|
|
42
|
-
function sightingsFrom(cwd, part,
|
|
43
|
-
return
|
|
54
|
+
function sightingsFrom(cwd, part, top) {
|
|
55
|
+
return top !== null ? [...readTree(top, part), ...readHistory(cwd, part)] : walkTree(cwd, part);
|
|
44
56
|
}
|
|
45
57
|
//# sourceMappingURL=cli.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli-allocate/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAoB,MAAM,qCAAqC,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli-allocate/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,9 +8,17 @@ export type HistoryBoundary = {
|
|
|
8
8
|
kind: "shallow";
|
|
9
9
|
before: string;
|
|
10
10
|
};
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* The added and removed lines of every reachable commit whose patch touched `SCN-<part>-`; a refusal
|
|
13
|
+
* when the refs it starts from, or the history git walks from them, cannot be read.
|
|
14
|
+
*/
|
|
12
15
|
export declare function readHistory(cwd: string, part: string): ScnSighting[];
|
|
13
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* The top of the repository `cwd` stands in, or null outside one. A `.git` at or above `cwd` that git
|
|
18
|
+
* will not open is a repository allocate cannot read, refused naming it, never read as no repository:
|
|
19
|
+
* that would state "no history was read" about a directory whose history exists. No git to ask at all
|
|
20
|
+
* is still no repository, a boundary said beside the answer.
|
|
21
|
+
*/
|
|
14
22
|
export declare function repositoryTop(cwd: string): string | null;
|
|
15
23
|
/** Whether `cwd` stands in a git repository. */
|
|
16
24
|
export declare function isGitRepository(cwd: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readHistory.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/readHistory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"readHistory.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/readHistory.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAIvE,+GAA+G;AAC/G,MAAM,MAAM,eAAe,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAQxH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAapE;AAQD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOxD;AAWD,gDAAgD;AAChD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAQ9D;AAaD,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAQlE"}
|
|
@@ -7,17 +7,32 @@
|
|
|
7
7
|
* cannot be unwritten, so one stray mention would move the answer for good. The format asks git for
|
|
8
8
|
* each commit's abbreviated hash and nothing else, so a sighting can name the commit that carried it.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Git is handed the part's prefix alone, which holds no regex syntax. Which words on a line are
|
|
11
|
+
* scenario codes is core's question, answered with the shared grammar, because git's regex dialect
|
|
12
|
+
* has no `\d` and matches nothing when given one, silently.
|
|
11
13
|
*/
|
|
12
14
|
import { spawnSync } from "node:child_process";
|
|
13
|
-
import { readFileSync } from "node:fs";
|
|
15
|
+
import { lstatSync, readFileSync } from "node:fs";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
import { proveRefsReadable } from "./refStore.js";
|
|
18
|
+
import { readOrRefuse, refuse, UnreadableInput } from "./unreadable.js";
|
|
14
19
|
/** A long history's patches can be large; the default buffer would truncate them. */
|
|
15
20
|
const MAX_OUTPUT_BYTES = 1024 * 1024 * 1024;
|
|
16
21
|
/** The marker the format puts before each commit's hash, a byte no patch line starts with. */
|
|
17
22
|
const COMMIT_MARKER = "\u0001";
|
|
18
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The added and removed lines of every reachable commit whose patch touched `SCN-<part>-`; a refusal
|
|
25
|
+
* when the refs it starts from, or the history git walks from them, cannot be read.
|
|
26
|
+
*/
|
|
19
27
|
export function readHistory(cwd, part) {
|
|
28
|
+
proveRefsReadable(cwd);
|
|
20
29
|
const result = spawnSync("git", ["log", "--all", "-p", "-U0", "--no-color", "--no-ext-diff", "--format=%x01%h", "-G", `SCN-${part}-`], { cwd, encoding: "utf8", maxBuffer: MAX_OUTPUT_BYTES });
|
|
30
|
+
if (result.error !== undefined)
|
|
31
|
+
refuse("the repository's history", cwd, result.error);
|
|
32
|
+
if (result.status !== 0) {
|
|
33
|
+
const said = result.stderr.split("\n")[0] ?? "";
|
|
34
|
+
throw new UnreadableInput("the repository's history", cwd, `git log exited ${result.status}: ${said}`);
|
|
35
|
+
}
|
|
21
36
|
return diffLineSightings(result.stdout);
|
|
22
37
|
}
|
|
23
38
|
/** Git's answer in `cwd`, trimmed, or null when git refuses (outside a repository, or no git at all). */
|
|
@@ -25,9 +40,31 @@ function gitAnswer(cwd, args) {
|
|
|
25
40
|
const result = spawnSync("git", args, { cwd, encoding: "utf8" });
|
|
26
41
|
return result.status === 0 ? result.stdout.trim() : null;
|
|
27
42
|
}
|
|
28
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* The top of the repository `cwd` stands in, or null outside one. A `.git` at or above `cwd` that git
|
|
45
|
+
* will not open is a repository allocate cannot read, refused naming it, never read as no repository:
|
|
46
|
+
* that would state "no history was read" about a directory whose history exists. No git to ask at all
|
|
47
|
+
* is still no repository, a boundary said beside the answer.
|
|
48
|
+
*/
|
|
29
49
|
export function repositoryTop(cwd) {
|
|
30
|
-
|
|
50
|
+
const result = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
|
|
51
|
+
if (result.status === 0)
|
|
52
|
+
return result.stdout.trim();
|
|
53
|
+
const gitEntry = result.error === undefined ? gitEntryAtOrAbove(cwd) : null;
|
|
54
|
+
if (gitEntry === null)
|
|
55
|
+
return null;
|
|
56
|
+
const said = result.stderr.split("\n")[0] ?? "";
|
|
57
|
+
throw new UnreadableInput("the repository", gitEntry, `git rev-parse exited ${result.status}: ${said}`);
|
|
58
|
+
}
|
|
59
|
+
/** The `.git` git's own discovery would find from `dir`: the nearest at or above it, of any kind; else null. */
|
|
60
|
+
function gitEntryAtOrAbove(dir) {
|
|
61
|
+
for (let at = dir;; at = dirname(at)) {
|
|
62
|
+
const entry = join(at, ".git");
|
|
63
|
+
if (lstatSync(entry, { throwIfNoEntry: false }) !== undefined)
|
|
64
|
+
return entry;
|
|
65
|
+
if (dirname(at) === at)
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
31
68
|
}
|
|
32
69
|
/** Whether `cwd` stands in a git repository. */
|
|
33
70
|
export function isGitRepository(cwd) {
|
|
@@ -40,7 +77,9 @@ export function historyBoundaryOf(cwd) {
|
|
|
40
77
|
if (gitAnswer(cwd, ["rev-parse", "--is-shallow-repository"]) !== "true")
|
|
41
78
|
return { kind: "complete" };
|
|
42
79
|
const shallowFile = gitAnswer(cwd, ["rev-parse", "--git-path", "shallow"]) ?? "";
|
|
43
|
-
const
|
|
80
|
+
const shallowPath = shallowFile.startsWith("/") ? shallowFile : `${cwd}/${shallowFile}`;
|
|
81
|
+
const record = readOrRefuse("the repository's history", shallowPath, () => readFileSync(shallowPath, "utf8"));
|
|
82
|
+
const edge = record.split("\n")[0] ?? "";
|
|
44
83
|
return { kind: "shallow", before: gitAnswer(cwd, ["rev-parse", "--short", edge]) ?? edge };
|
|
45
84
|
}
|
|
46
85
|
/** The added and removed lines of `git log -p` output, each with where it stands. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readHistory.js","sourceRoot":"","sources":["../../src/cli-allocate/readHistory.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"readHistory.js","sourceRoot":"","sources":["../../src/cli-allocate/readHistory.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readParts.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/readParts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"readParts.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/readParts.ts"],"names":[],"mappings":"AASA,oGAAoG;AACpG,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAM7D"}
|
|
@@ -5,13 +5,16 @@
|
|
|
5
5
|
import { readdirSync, readFileSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { extractFeatureCode } from "../ingest/ingestScenarios.js";
|
|
8
|
+
import { readOrRefuse, refuse } from "./unreadable.js";
|
|
8
9
|
/** The header codes of the feature files in `featuresDir`; none when there is no such directory. */
|
|
9
10
|
export function readHeaderCodes(featuresDir) {
|
|
10
11
|
return featureFilesIn(featuresDir).flatMap((name) => {
|
|
11
|
-
const
|
|
12
|
+
const path = join(featuresDir, name);
|
|
13
|
+
const code = extractFeatureCode(readOrRefuse("a feature file", path, () => readFileSync(path, "utf8")));
|
|
12
14
|
return code === null ? [] : [code];
|
|
13
15
|
});
|
|
14
16
|
}
|
|
17
|
+
/** The feature files `dir` lists; none when it is not there, and a refusal when it cannot be listed. */
|
|
15
18
|
function featureFilesIn(dir) {
|
|
16
19
|
try {
|
|
17
20
|
return readdirSync(dir).filter((name) => name.endsWith(".feature"));
|
|
@@ -19,7 +22,7 @@ function featureFilesIn(dir) {
|
|
|
19
22
|
catch (err) {
|
|
20
23
|
if (err.code === "ENOENT")
|
|
21
24
|
return [];
|
|
22
|
-
|
|
25
|
+
return refuse("the feature corpus", dir, err);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
//# sourceMappingURL=readParts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readParts.js","sourceRoot":"","sources":["../../src/cli-allocate/readParts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"readParts.js","sourceRoot":"","sources":["../../src/cli-allocate/readParts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEvD,oGAAoG;AACpG,MAAM,UAAU,eAAe,CAAC,WAAmB;IACjD,OAAO,cAAc,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,kBAAkB,CAAC,YAAY,CAAC,gBAAgB,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACxG,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,wGAAwG;AACxG,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QAChE,OAAO,MAAM,CAAC,oBAAoB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAChD,CAAC;AACH,CAAC"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { ScnSighting } from "@3f-consulting/spec-controller-core";
|
|
2
2
|
/**
|
|
3
|
-
* Every line in the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* written a scenario, and it is not committed yet.
|
|
3
|
+
* Every line in the repository's tree carrying `SCN-<part>-`, with its path from `top`. The tree is
|
|
4
|
+
* what git lists: tracked files, and untracked files the repository does not ignore, since an author
|
|
5
|
+
* allocates because they have just written a scenario, and it is not committed yet.
|
|
7
6
|
*/
|
|
8
|
-
export declare function readTree(
|
|
7
|
+
export declare function readTree(top: string, part: string): ScnSighting[];
|
|
9
8
|
/**
|
|
10
9
|
* Whether a walk without git enters a directory: never a package store, never a hidden directory.
|
|
11
10
|
* An installed package carries its own codes (spec-controller's own dist carries hundreds), which
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readTree.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/readTree.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"readTree.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/readTree.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AASvE;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAKjE;AAuDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,yGAAyG;AACzG,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAKlE"}
|
|
@@ -1,35 +1,82 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The tree half of the allocator's read: every line of every text file that mentions the part.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* In a repository, git says which files are the tree and allocate reads each of them itself. Git
|
|
5
|
+
* skips a file or directory it cannot read and still exits 0, so a code carried only there would be
|
|
6
|
+
* handed out again; reading the files here, and listing every directory git had to list, turns each
|
|
7
|
+
* such path into a refusal instead. Which words on a line are scenario codes is core's question,
|
|
8
|
+
* answered with the shared grammar.
|
|
7
9
|
*/
|
|
8
10
|
import { spawnSync } from "node:child_process";
|
|
9
|
-
import { readdirSync, readFileSync } from "node:fs";
|
|
11
|
+
import { accessSync, constants, lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
10
12
|
import { join, relative, sep } from "node:path";
|
|
11
|
-
|
|
13
|
+
import { readOrRefuse, refuse, UnreadableInput } from "./unreadable.js";
|
|
14
|
+
/** Git's listing can be large on a big repository; the default buffer would truncate it. */
|
|
12
15
|
const MAX_OUTPUT_BYTES = 1024 * 1024 * 1024;
|
|
16
|
+
/** How far into a file git looks for a NUL before it calls the file binary and searches none of it. */
|
|
17
|
+
const BINARY_PROBE_BYTES = 8000;
|
|
13
18
|
/**
|
|
14
|
-
* Every line in the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* written a scenario, and it is not committed yet.
|
|
19
|
+
* Every line in the repository's tree carrying `SCN-<part>-`, with its path from `top`. The tree is
|
|
20
|
+
* what git lists: tracked files, and untracked files the repository does not ignore, since an author
|
|
21
|
+
* allocates because they have just written a scenario, and it is not committed yet.
|
|
18
22
|
*/
|
|
19
|
-
export function readTree(
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
export function readTree(top, part) {
|
|
24
|
+
const listed = gitListing(top, ["ls-files", "-z", "--cached", "--others", "--exclude-standard"]);
|
|
25
|
+
const ignored = gitListing(top, ["ls-files", "-z", "--others", "--ignored", "--exclude-standard", "--directory"]);
|
|
26
|
+
proveListable(top, "", new Set(ignored.filter(isDirectoryEntry).map((entry) => entry.slice(0, -1))));
|
|
27
|
+
return listed.filter((entry) => !isDirectoryEntry(entry)).flatMap((path) => sightingsInListed(top, path, `SCN-${part}-`));
|
|
28
|
+
}
|
|
29
|
+
/** The paths a `git ls-files -z` run lists, or the refusal when git could not list them. */
|
|
30
|
+
function gitListing(top, args) {
|
|
31
|
+
const result = spawnSync("git", args, { cwd: top, encoding: "utf8", maxBuffer: MAX_OUTPUT_BYTES });
|
|
32
|
+
if (result.error !== undefined)
|
|
33
|
+
refuse("the tree", top, result.error);
|
|
34
|
+
if (result.status !== 0) {
|
|
35
|
+
const said = result.stderr.split("\n")[0] ?? "";
|
|
36
|
+
throw new UnreadableInput("the tree", top, `git ls-files exited ${result.status}: ${said}`);
|
|
37
|
+
}
|
|
38
|
+
return result.stdout.split("\0").filter((entry) => entry !== "");
|
|
39
|
+
}
|
|
40
|
+
/** A listed directory rather than a file: a repository nested in the tree, or a wholly ignored one. */
|
|
41
|
+
function isDirectoryEntry(entry) {
|
|
42
|
+
return entry.endsWith("/");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Every directory git had to list to answer, listed and entered here too, so one it could not list
|
|
46
|
+
* is refused rather than skipped. Ignored directories and nested repositories are not the tree.
|
|
47
|
+
*/
|
|
48
|
+
function proveListable(top, dir, ignored) {
|
|
49
|
+
const path = dir === "" ? top : join(top, dir);
|
|
50
|
+
const entries = readOrRefuse("a directory in the tree", path, () => listedAndEntered(path));
|
|
51
|
+
if (dir !== "" && entries.some((entry) => entry.name === ".git"))
|
|
52
|
+
return;
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const child = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
55
|
+
if (entry.isDirectory() && entry.name !== ".git" && !ignored.has(child))
|
|
56
|
+
proveListable(top, child, ignored);
|
|
57
|
+
}
|
|
26
58
|
}
|
|
27
|
-
/**
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
59
|
+
/** A directory's entries, when it may be both listed and entered. */
|
|
60
|
+
function listedAndEntered(path) {
|
|
61
|
+
accessSync(path, constants.R_OK | constants.X_OK);
|
|
62
|
+
return readdirSync(path, { withFileTypes: true });
|
|
63
|
+
}
|
|
64
|
+
/** The sightings in one file git lists: none when it has left the working tree or is not a file. */
|
|
65
|
+
function sightingsInListed(top, path, needle) {
|
|
66
|
+
const file = join(top, path);
|
|
67
|
+
const kind = readOrRefuse("a file in the tree", file, () => lstatSync(file, { throwIfNoEntry: false }));
|
|
68
|
+
if (kind === undefined || !kind.isFile())
|
|
69
|
+
return [];
|
|
70
|
+
return linesCarrying(path, readOrRefuse("a file in the tree", file, () => readFileSync(file)), needle);
|
|
71
|
+
}
|
|
72
|
+
/** The lines of a file's content that carry `needle`; a binary file, by git's rule, carries none. */
|
|
73
|
+
function linesCarrying(path, content, needle) {
|
|
74
|
+
if (content.subarray(0, BINARY_PROBE_BYTES).includes(0))
|
|
31
75
|
return [];
|
|
32
|
-
return
|
|
76
|
+
return content
|
|
77
|
+
.toString("utf8")
|
|
78
|
+
.split("\n")
|
|
79
|
+
.flatMap((line, i) => (line.includes(needle) ? [{ path, line: i + 1, text: line }] : []));
|
|
33
80
|
}
|
|
34
81
|
/**
|
|
35
82
|
* Whether a walk without git enters a directory: never a package store, never a hidden directory.
|
|
@@ -41,23 +88,19 @@ export function isWalkedDirectory(name) {
|
|
|
41
88
|
}
|
|
42
89
|
/** Every line carrying `SCN-<part>-` in the text files under `root`, found by walking it without git. */
|
|
43
90
|
export function walkTree(root, part) {
|
|
44
|
-
return filesUnder(root).flatMap((file) =>
|
|
91
|
+
return filesUnder(root).flatMap((file) => {
|
|
92
|
+
const content = readOrRefuse("a file in the tree", file, () => readFileSync(file));
|
|
93
|
+
return linesCarrying(relative(root, file).split(sep).join("/"), content, `SCN-${part}-`);
|
|
94
|
+
});
|
|
45
95
|
}
|
|
46
|
-
/** Every file under `dir`, entering only the directories a walk enters. */
|
|
96
|
+
/** Every file under `dir`, entering only the directories a walk enters; a refusal for one it cannot list. */
|
|
47
97
|
function filesUnder(dir) {
|
|
48
|
-
|
|
98
|
+
const entries = readOrRefuse("a directory in the tree", dir, () => readdirSync(dir, { withFileTypes: true }));
|
|
99
|
+
return entries.flatMap((entry) => {
|
|
49
100
|
const path = join(dir, entry.name);
|
|
50
101
|
if (entry.isDirectory())
|
|
51
102
|
return isWalkedDirectory(entry.name) ? filesUnder(path) : [];
|
|
52
103
|
return entry.isFile() ? [path] : [];
|
|
53
104
|
});
|
|
54
105
|
}
|
|
55
|
-
/** The lines of a text file that carry `needle`; a binary file carries none. */
|
|
56
|
-
function sightingsIn(root, file, needle) {
|
|
57
|
-
const text = readFileSync(file, "utf8");
|
|
58
|
-
if (text.includes("\0"))
|
|
59
|
-
return [];
|
|
60
|
-
const path = relative(root, file).split(sep).join("/");
|
|
61
|
-
return text.split("\n").flatMap((line, i) => (line.includes(needle) ? [{ path, line: i + 1, text: line }] : []));
|
|
62
|
-
}
|
|
63
106
|
//# sourceMappingURL=readTree.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readTree.js","sourceRoot":"","sources":["../../src/cli-allocate/readTree.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"readTree.js","sourceRoot":"","sources":["../../src/cli-allocate/readTree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refStore.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/refStore.ts"],"names":[],"mappings":"AAiBA,0GAA0G;AAC1G,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAInD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The refs the history read starts from, proved readable before git is asked for the history.
|
|
3
|
+
*
|
|
4
|
+
* Git walks history from every ref, and a ref store it cannot read is not an error to git: with
|
|
5
|
+
* `refs/heads` unreadable, `git log --all` exits 0, writes nothing to stderr, and simply walks from
|
|
6
|
+
* fewer refs, so a deleted code only an unread branch reaches would be handed out again. Reading the
|
|
7
|
+
* store here is the only way that case is ever seen. It reads, and never interprets: whether the
|
|
8
|
+
* store is loose files, `packed-refs` or a reftable, every one of them git would read is read here.
|
|
9
|
+
*/
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { readOrRefuse } from "./unreadable.js";
|
|
14
|
+
/** Where each ref store lives under a git directory, whichever format the repository keeps. */
|
|
15
|
+
const REF_STORES = ["refs", "packed-refs", "reftable"];
|
|
16
|
+
/** Refuse, as history allocate cannot read, a ref store under the repository at `cwd` it may not read. */
|
|
17
|
+
export function proveRefsReadable(cwd) {
|
|
18
|
+
for (const gitDir of gitDirsOf(cwd)) {
|
|
19
|
+
for (const store of REF_STORES)
|
|
20
|
+
readEveryEntry(join(gitDir, store));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** The repository's common git directory and its own, which differ only in a linked worktree. */
|
|
24
|
+
function gitDirsOf(cwd) {
|
|
25
|
+
const result = spawnSync("git", ["rev-parse", "--path-format=absolute", "--git-common-dir", "--git-dir"], {
|
|
26
|
+
cwd,
|
|
27
|
+
encoding: "utf8",
|
|
28
|
+
});
|
|
29
|
+
if (result.status !== 0)
|
|
30
|
+
return [];
|
|
31
|
+
return [...new Set(result.stdout.split("\n").filter((line) => line !== ""))];
|
|
32
|
+
}
|
|
33
|
+
/** Read `path` whole: a file's content, or every entry under a directory; nothing when it is not there. */
|
|
34
|
+
function readEveryEntry(path) {
|
|
35
|
+
const kind = readOrRefuse("the repository's history", path, () => lstatSync(path, { throwIfNoEntry: false }));
|
|
36
|
+
if (kind === undefined)
|
|
37
|
+
return;
|
|
38
|
+
if (kind.isDirectory()) {
|
|
39
|
+
const names = readOrRefuse("the repository's history", path, () => readdirSync(path));
|
|
40
|
+
for (const name of names)
|
|
41
|
+
readEveryEntry(join(path, name));
|
|
42
|
+
}
|
|
43
|
+
else if (kind.isFile()) {
|
|
44
|
+
readOrRefuse("the repository's history", path, () => readFileSync(path));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=refStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refStore.js","sourceRoot":"","sources":["../../src/cli-allocate/refStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,+FAA+F;AAC/F,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,CAAU,CAAC;AAEhE,0GAA0G;AAC1G,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;QACpC,KAAK,MAAM,KAAK,IAAI,UAAU;YAAE,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED,iGAAiG;AACjG,SAAS,SAAS,CAAC,GAAW;IAC5B,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,WAAW,CAAC,EAAE;QACxG,GAAG;QACH,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,2GAA2G;AAC3G,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,IAAI,GAAG,YAAY,CAAC,0BAA0B,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAC9G,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,YAAY,CAAC,0BAA0B,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QACtF,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAC7D,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACzB,YAAY,CAAC,0BAA0B,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A path allocate is meant to read and cannot. It is the invocation being wrong, the same as a
|
|
3
|
+
* malformed request, so it is refused as a usage error naming the path and the reason, and no code
|
|
4
|
+
* is given: an answer computed without the path could hand out a code the unread path carries.
|
|
5
|
+
*/
|
|
6
|
+
/** What allocate could not read: which input it was, where (when the read had a path), and the reason. */
|
|
7
|
+
export declare class UnreadableInput extends Error {
|
|
8
|
+
readonly subject: string;
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly reason: string;
|
|
11
|
+
readonly name = "UnreadableInput";
|
|
12
|
+
constructor(subject: string, path: string, reason: string);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The read's answer, or the refusal when the operating system refuses it. Any refusal the OS gives
|
|
16
|
+
* is the same fault here: not permitted, not the kind of path the read expects, or not there. A
|
|
17
|
+
* throw that carries no OS code is not a read failing, and is left to surface.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readOrRefuse<T>(subject: string, path: string, read: () => T): T;
|
|
20
|
+
/** Throw the refusal a failed read of `path` is, or the throw itself when it is not a read failing. */
|
|
21
|
+
export declare function refuse(subject: string, path: string, err: unknown): never;
|
|
22
|
+
/** The one stderr line a refusal is written as, in allocate's usage-error form. */
|
|
23
|
+
export declare function refusalLine(refusal: UnreadableInput): string;
|
|
24
|
+
//# sourceMappingURL=unreadable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unreadable.d.ts","sourceRoot":"","sources":["../../src/cli-allocate/unreadable.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,0GAA0G;AAC1G,qBAAa,eAAgB,SAAQ,KAAK;IAItC,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM;IALzB,SAAkB,IAAI,qBAAqB;gBAGhC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM;CAI1B;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAM/E;AAED,uGAAuG;AACvG,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,KAAK,CAIzE;AAED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A path allocate is meant to read and cannot. It is the invocation being wrong, the same as a
|
|
3
|
+
* malformed request, so it is refused as a usage error naming the path and the reason, and no code
|
|
4
|
+
* is given: an answer computed without the path could hand out a code the unread path carries.
|
|
5
|
+
*/
|
|
6
|
+
/** What allocate could not read: which input it was, where (when the read had a path), and the reason. */
|
|
7
|
+
export class UnreadableInput extends Error {
|
|
8
|
+
subject;
|
|
9
|
+
path;
|
|
10
|
+
reason;
|
|
11
|
+
name = "UnreadableInput";
|
|
12
|
+
constructor(subject, path, reason) {
|
|
13
|
+
super(path === "" ? `${subject} could not be read (${reason})` : `${subject} could not be read: ${path} (${reason})`);
|
|
14
|
+
this.subject = subject;
|
|
15
|
+
this.path = path;
|
|
16
|
+
this.reason = reason;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The read's answer, or the refusal when the operating system refuses it. Any refusal the OS gives
|
|
21
|
+
* is the same fault here: not permitted, not the kind of path the read expects, or not there. A
|
|
22
|
+
* throw that carries no OS code is not a read failing, and is left to surface.
|
|
23
|
+
*/
|
|
24
|
+
export function readOrRefuse(subject, path, read) {
|
|
25
|
+
try {
|
|
26
|
+
return read();
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
return refuse(subject, path, err);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Throw the refusal a failed read of `path` is, or the throw itself when it is not a read failing. */
|
|
33
|
+
export function refuse(subject, path, err) {
|
|
34
|
+
const errno = err;
|
|
35
|
+
if (!(err instanceof Error) || typeof errno.code !== "string")
|
|
36
|
+
throw err;
|
|
37
|
+
throw new UnreadableInput(subject, errno.path ?? path, err.message);
|
|
38
|
+
}
|
|
39
|
+
/** The one stderr line a refusal is written as, in allocate's usage-error form. */
|
|
40
|
+
export function refusalLine(refusal) {
|
|
41
|
+
return `spec-controller allocate: usage error — ${refusal.message}`;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=unreadable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unreadable.js","sourceRoot":"","sources":["../../src/cli-allocate/unreadable.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,0GAA0G;AAC1G,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAI7B;IACA;IACA;IALO,IAAI,GAAG,iBAAiB,CAAC;IAE3C,YACW,OAAe,EACf,IAAY,EACZ,MAAc;QAEvB,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,uBAAuB,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,uBAAuB,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC;QAJ7G,YAAO,GAAP,OAAO,CAAQ;QACf,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAAQ;IAGzB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAI,OAAe,EAAE,IAAY,EAAE,IAAa;IAC1E,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,MAAM,CAAC,OAAe,EAAE,IAAY,EAAE,GAAY;IAChE,MAAM,KAAK,GAAG,GAA4B,CAAC;IAC3C,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,MAAM,GAAG,CAAC;IACzE,MAAM,IAAI,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,WAAW,CAAC,OAAwB;IAClD,OAAO,2CAA2C,OAAO,CAAC,OAAO,EAAE,CAAC;AACtE,CAAC"}
|
|
@@ -62,8 +62,9 @@ export declare function checkInputsExist(args: Record<string, string>, exists: (
|
|
|
62
62
|
* PURE and `@unit`-testable like its sibling guards: reads the parsed args + an injected
|
|
63
63
|
* `read` fn (no fs), returns the FIRST supplied-but-unparseable `(flag, path, failure)` as
|
|
64
64
|
* a typed error naming all three — routed to stderr + exit 2 by `refuseUnreadableInputs` — else null.
|
|
65
|
-
* An omitted flag is not an error, and
|
|
66
|
-
* check)
|
|
65
|
+
* An omitted flag is not an error, and a file that cannot be read at all (a race after the
|
|
66
|
+
* existence check, a permission refused) is refused as one that could not be read, never as JSON
|
|
67
|
+
* that did not parse.
|
|
67
68
|
*/
|
|
68
69
|
export declare function checkInputsParse(args: Record<string, string>, read: (path: string) => string): string | null;
|
|
69
70
|
/**
|