@mark1russell7/rigor-bench 0.2.21 → 0.2.23
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 +74 -0
- package/build/code-stamp.d.ts +66 -0
- package/build/code-stamp.d.ts.map +1 -0
- package/build/code-stamp.js +215 -0
- package/build/code-stamp.js.map +1 -0
- package/build/index.d.ts +66 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +105 -1
- package/build/index.js.map +1 -1
- package/build/supervise.js +1 -1
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -69,6 +69,8 @@ v.comparison; // the cases that gain and dro
|
|
|
69
69
|
| `childLine(stdout)` | From `/fresh`: the last JSON line of a child, with its time `ms`, its `hash` and the reason of a skip. |
|
|
70
70
|
| `pairOrder(pairs, round, caseIndex)` | From `/fresh`: the order of the pairs of one case in one round, and the first arm of each pair. |
|
|
71
71
|
| `freshRun(options)` | From `/fresh`: the records of each pair, with each arm in its own fresh child process. |
|
|
72
|
+
| `codeStamp(options)` | From `/code-stamp`: the stamp of the code of a run, from the code that its entries reach, the lock closure of its engines and its data files. |
|
|
73
|
+
| `lockClosure(text, importer, names)` | From `/code-stamp`: each package that an engine pulls in, from a pnpm lock file. |
|
|
72
74
|
| `superviseCases(options)` | From `/supervise`: each case in a child process, held to a time budget, with a hard stop and a resume. |
|
|
73
75
|
| `caseProgress(file)` | From `/supervise`: the writer of the progress file of a child. |
|
|
74
76
|
| `startSupervisedProcess(argv, options)` | From `/supervise`: a child process that `superviseCases` can stop with its tree. |
|
|
@@ -400,6 +402,78 @@ A run of many jobs sets `roundOffset` to `job * rounds`. Then the merged records
|
|
|
400
402
|
|
|
401
403
|
A rotation of N arms turns the order of two arms only when they are N/2 apart in the rotation. For 4 arms, two arms 1 apart come in the order 3:1 over 4 rounds. Thus each pair of `freshRun` has its own two children, and the order of each pair turns each round.
|
|
402
404
|
|
|
405
|
+
## A result that changes on purpose
|
|
406
|
+
|
|
407
|
+
A bench compares two arms, and two arms with different results do not do the same work. So a difference stops the merge. A pull request can change a result on purpose. Without a stated path, its author edits the case, and no gate reads that edit. A row in the diff gives that path, and it names the exact change.
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
import { resultRows, resultVerdict } from "@mark1russell7/rigor-bench";
|
|
411
|
+
|
|
412
|
+
const verdict = resultVerdict(
|
|
413
|
+
[{ case: "facts of a tree", base: "9f3a", head: "c81e" }], // the hash of the result of each case in the two arms
|
|
414
|
+
{ base: resultRows(rowsOfTheBase), head: resultRows(rowsOfTheHead) }, // the parsed file of rows in each arm
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
verdict.outcome; // "pass" or "fail"
|
|
418
|
+
verdict.lines; // the lines that the check prints
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
A row has four fields: `case`, `base`, `head` and `reason`. The fields `base` and `head` are hashes that the caller makes, so a large result stays out of the file. A reviewer can read a row against the run. A refused line prints the row to add. These rules stop the reuse of a row:
|
|
422
|
+
|
|
423
|
+
- **The hashes bind the row to one change.** The same row against one other result reads `refused`, and the line says which side moved: the base, the head or both. The fields have an order, so a row of a change does not accept its revert.
|
|
424
|
+
- **A row counts only if it is new.** A row that the base also has is spent and accepts nothing, even on the same change. The verdict keeps the array `spent`, and it prints one line with the count.
|
|
425
|
+
- **A new row that accepts nothing fails the verdict.** A row of a case with one result reads `unused`. Otherwise it merges and is spent, and a later true change is refused.
|
|
426
|
+
- **A row of a case with no result fails the verdict.** It reads `absent`, because a missing read is no pass. The case can be skipped or filtered out, so the line does not tell the author to delete the row.
|
|
427
|
+
- **A change that comes again needs two pull requests.** The first one deletes the spent row, and the second one adds it. A revert can delete the row in the same pull request.
|
|
428
|
+
|
|
429
|
+
**Time.** The verdict reads results only. An accepted result buys no time reading. Keep the accepted case in `freshRun`, so it gets its head and A/A pairs and its normal time verdict. The time of the new result counts. The hash rule of `freshRun` is per arm, so two arms with different hashes run. The test "refuses two hashes of one arm on one case, and permits other hashes for other arms" pins this rule.
|
|
430
|
+
|
|
431
|
+
## The stamp of the code of a run
|
|
432
|
+
|
|
433
|
+
The entry `@mark1russell7/rigor-bench/code-stamp` is for Node only. It uses the optional peer `esbuild`. Add `esbuild` to the dependencies of the caller, so that its lock file pins the version.
|
|
434
|
+
|
|
435
|
+
A run keeps a saved record when the stamp of the record equals the stamp of the run. A version number is a poor stamp. A release that changes no result throws away each record, and a change with no release keeps wrong records. The function `codeStamp` hashes what can change a result.
|
|
436
|
+
|
|
437
|
+
```ts
|
|
438
|
+
import { codeStamp } from "@mark1russell7/rigor-bench/code-stamp";
|
|
439
|
+
|
|
440
|
+
const stamp = await codeStamp({
|
|
441
|
+
cwd: "packages/bench-of-my-repo",
|
|
442
|
+
entries: ["src/runner.ts", "src/cover.ts"],
|
|
443
|
+
engines: { names: ["my-engine"], lockFile: "../../pnpm-lock.yaml", importer: "packages/bench-of-my-repo" },
|
|
444
|
+
dataFiles: ["corpus/corpus.json"],
|
|
445
|
+
reads: ["readFileSync"],
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
| Part of `stamp.parts` | What it reads | A change that moves it |
|
|
450
|
+
| --- | --- | --- |
|
|
451
|
+
| `code` | The bundle of the entries after a tree shake, with the engines outside | A token that the entries reach, a new export of an entry, a rename |
|
|
452
|
+
| `engines` | The version and the integrity of each package in the lock closure of each engine | An engine version, a transitive package of an engine |
|
|
453
|
+
| `data` | The name and the hash of each file in `dataFiles` | A byte or the name of such a file |
|
|
454
|
+
| `bundler` | The version of `esbuild`. This part is not in the hash | A new version in the lock file of the caller |
|
|
455
|
+
| `node` | The Node major | Another major, which can change a float or a sort |
|
|
456
|
+
|
|
457
|
+
These changes keep the stamp: a comment, blank space, line ends, a type and a version in a package file. An export that the entries do not reach and another folder for the same files also keep it.
|
|
458
|
+
|
|
459
|
+
The same tree gives the same stamp on Linux and on Windows. A slash in an option can have either direction. The hash of a text data file reads each line end as LF, and a file with a NUL byte hashes as its bytes. So a parser that reads a line end as data can read another result with an equal stamp.
|
|
460
|
+
|
|
461
|
+
The stamp holds no computer. Each record keeps the computer as a field, because a time depends on it and a result does not.
|
|
462
|
+
|
|
463
|
+
### What a bundle cannot see
|
|
464
|
+
|
|
465
|
+
- **A file that the code reads when it runs.** The bundle holds no such file. So the function looks for the words of `CODE_STAMP_READ_WORDS` in the bundle, for example `readFileSync`, `fetch` and `process.env`. A word that the option `reads` does not state refuses the stamp, and so does a stated word that the bundle does not hold. List each file that such a read opens in `dataFiles`.
|
|
466
|
+
- **The name of a file that a program makes at run time.** A word tells that a read exists, not which file it opens. The list in `dataFiles` is the statement of the caller.
|
|
467
|
+
- **The code below an engine.** An engine stays outside the bundle, so `lockClosure` hashes the resolved closure of the engine from the lock file. The name of the engine alone is not sufficient. It reads pnpm lock files of version 9 and refuses another version.
|
|
468
|
+
|
|
469
|
+
A workspace package is not an engine: `lockClosure` refuses it, because its code enters the bundle.
|
|
470
|
+
|
|
471
|
+
### The limits of the stamp
|
|
472
|
+
|
|
473
|
+
- A rename of an identifier moves the stamp, and so does the rename of an entry file. This gives a false rerun, not a false keep.
|
|
474
|
+
- A top-level statement with an effect stays in the bundle when its module has no `"sideEffects": false`. A change of that statement moves the stamp, also when no result depends on it.
|
|
475
|
+
- A new version of the bundler that gives an equal bundle keeps the stamp and the records. A new version that gives another bundle moves `code`, so it keeps no record, also when no result can change. This gives a false rerun, not a false keep. The field `bundler` of `parts` tells the cause. A caller with long runs holds automatic updates of the bundler in its own update settings. It merges an update when its next full run starts. The read of 2026-11-05 stamped 2 real entries (17 inputs) with 8 versions of the bundler, 0.27.2 to 0.28.2. Each of the 7 steps gave an equal bundle, so 0 of 7 steps gave a false rerun. The true rate is at most approximately 35 % at 95 %, and this read is of one tree only. Also, esbuild 0.27.4 crashed on a failed build of an entry in that read. That version is outside the peer range, so the read does not count it as a false rerun.
|
|
476
|
+
|
|
403
477
|
## Run cases with a time budget
|
|
404
478
|
|
|
405
479
|
The entry `@mark1russell7/rigor-bench/supervise` starts child processes, so it is for Node only.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** The engines of a run, which stay outside the bundle. The lock file gives what each engine pulls in. */
|
|
2
|
+
export interface CodeStampEngines {
|
|
3
|
+
/** The package names. A subpath of a name also stays outside the bundle. */
|
|
4
|
+
readonly names: readonly string[];
|
|
5
|
+
/** The path of the `pnpm-lock.yaml`, from `cwd`. */
|
|
6
|
+
readonly lockFile: string;
|
|
7
|
+
/** The key of the package in `importers` of the lock file, for example `packages/joint`. */
|
|
8
|
+
readonly importer: string;
|
|
9
|
+
}
|
|
10
|
+
/** The inputs of `codeStamp`. Each relative path starts at `cwd`. */
|
|
11
|
+
export interface CodeStampOptions {
|
|
12
|
+
readonly cwd: string;
|
|
13
|
+
/** The source files that a run starts from. Each export of these files stays in the bundle, so a new export of an entry moves the stamp. */
|
|
14
|
+
readonly entries: readonly string[];
|
|
15
|
+
readonly engines?: CodeStampEngines;
|
|
16
|
+
/**
|
|
17
|
+
* The files that the code reads when it runs, for example a corpus or a table. A bundle cannot see them, so each has its own hash.
|
|
18
|
+
* The hash of a text file reads each line end as LF, so a checkout with CRLF gives the same stamp. A file with a NUL byte hashes as its bytes.
|
|
19
|
+
*/
|
|
20
|
+
readonly dataFiles?: readonly string[];
|
|
21
|
+
/** The words of `CODE_STAMP_READ_WORDS` that the caller expects in the bundle. A difference in either direction refuses the stamp. */
|
|
22
|
+
readonly reads?: readonly string[];
|
|
23
|
+
/** The Node major of the stamp. The default is the major of this process. */
|
|
24
|
+
readonly node?: string;
|
|
25
|
+
}
|
|
26
|
+
/** One engine of a stamp: the version in the lock file, and each package of its closure with its peers. */
|
|
27
|
+
export interface EngineClosure {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly version: string;
|
|
30
|
+
readonly closure: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
/** The hash of each part of a stamp, so that a change of the stamp names its cause. */
|
|
33
|
+
export interface CodeStampParts {
|
|
34
|
+
readonly code: string;
|
|
35
|
+
readonly engines: string;
|
|
36
|
+
readonly data: string;
|
|
37
|
+
/** The name and the version of the bundler, for example `esbuild@0.28.2`. It tells the cause of a change of `code`, and it is not in the hash. */
|
|
38
|
+
readonly bundler: string;
|
|
39
|
+
readonly node: string;
|
|
40
|
+
}
|
|
41
|
+
/** The result of `codeStamp`. Only `hash` and `parts` are the stamp. The other fields tell what went in. */
|
|
42
|
+
export interface CodeStamp {
|
|
43
|
+
readonly hash: string;
|
|
44
|
+
readonly parts: CodeStampParts;
|
|
45
|
+
/** The source files that put at least one byte in the bundle, with paths from `cwd`. */
|
|
46
|
+
readonly inputs: readonly string[];
|
|
47
|
+
/** The words of `CODE_STAMP_READ_WORDS` in the bundle. */
|
|
48
|
+
readonly reads: readonly string[];
|
|
49
|
+
readonly engines: readonly EngineClosure[];
|
|
50
|
+
}
|
|
51
|
+
/** The words in a bundle that show an input from outside the bundle. A word inside a text also counts, so the scan can only say too much. */
|
|
52
|
+
export declare const CODE_STAMP_READ_WORDS: readonly string[];
|
|
53
|
+
/**
|
|
54
|
+
* This function gives the closure of each engine from the text of a pnpm lock file of version 9, in the order of `names`.
|
|
55
|
+
* A closure holds each package that the engine pulls in, with its peers, in sorted order.
|
|
56
|
+
* It refuses another lock version, an unknown importer or name, and a workspace package, which belongs in the bundle.
|
|
57
|
+
*/
|
|
58
|
+
export declare function lockClosure(lockText: string, importer: string, names: readonly string[]): readonly EngineClosure[];
|
|
59
|
+
/**
|
|
60
|
+
* This function gives the stamp of the code of a run. Only the options, the source files and the data files decide it, so another folder gives the same stamp.
|
|
61
|
+
* A change in comments, blank space, line ends or types alone keeps the stamp. A new export that the entries do not reach also keeps it.
|
|
62
|
+
* A change in a token that the entries reach moves `parts.code`. Identifiers stay as they are, so a rename also moves it. This gives a false rerun, not a false keep.
|
|
63
|
+
* A bundle cannot see a file that the code reads when it runs. So a word of `CODE_STAMP_READ_WORDS` that `reads` does not state refuses the stamp, and `dataFiles` lists the files.
|
|
64
|
+
*/
|
|
65
|
+
export declare function codeStamp(options: CodeStampOptions): Promise<CodeStamp>;
|
|
66
|
+
//# sourceMappingURL=code-stamp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-stamp.d.ts","names":[],"sources":["../src/code-stamp.ts"],"mappings":";iBAeiB;;WAEN;;WAEA;;WAEA;;;iBAIM;WACN;;WAEA;WACA,UAAU;;;;;WAKV;;WAEA;;WAEA;;;iBAIM;WACN;WACA;WACA;;;iBAIM;WACN;WACA;WACA;;WAEA;WACA;;;iBAIM;WACN;WACA,OAAO;;WAEP;;WAEA;WACA,kBAAkB;;;qBAIhB;;;;;;wBA0FG,YAAY,kBAAkB,kBAAkB,oCAAoC;;;;;;;wBA4D9E,UAAU,SAAS,mBAAmB,QAAQ"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
4
|
+
// src/read-words.ts
|
|
5
|
+
/** The test of a word against the text of a bundle. This file is internal, so its name is not in the public API. */
|
|
6
|
+
const isWordCharacter = (character) => character !== void 0 && /\w/.test(character);
|
|
7
|
+
/**
|
|
8
|
+
* This function tells if a text holds a word. Each character of the word is literal, so no character of the word needs an escape.
|
|
9
|
+
* A word edge that is a word character must also be a word boundary in the text.
|
|
10
|
+
* So `fetch` is not in `fetcher`, and `import(` is not in `reimport(`.
|
|
11
|
+
* An empty word is in no text.
|
|
12
|
+
*/
|
|
13
|
+
function hasWord(text, word) {
|
|
14
|
+
if (word === "") return false;
|
|
15
|
+
const startEdge = isWordCharacter(word[0]);
|
|
16
|
+
const endEdge = isWordCharacter(word.at(-1));
|
|
17
|
+
for (let at = text.indexOf(word); at !== -1; at = text.indexOf(word, at + 1)) {
|
|
18
|
+
const startOk = !startEdge || !isWordCharacter(text[at - 1]);
|
|
19
|
+
const endOk = !endEdge || !isWordCharacter(text[at + word.length]);
|
|
20
|
+
if (startOk && endOk) return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
// src/code-stamp.ts
|
|
25
|
+
/**
|
|
26
|
+
* The stamp of the code of a run, for Node only. A run keeps a saved record when the stamp of the record equals the stamp of the run.
|
|
27
|
+
* A version number is a poor stamp. A release that changes no result throws away each record. A change that has no release keeps wrong records.
|
|
28
|
+
* This entry hashes what can change a result. These are the code that the entries reach after a tree shake and the lock closure of the engines.
|
|
29
|
+
* The stated data files and the Node major are also in the hash.
|
|
30
|
+
* The bundler only makes the text that the hash reads, and no run uses that text. So its version is a part of the stamp and is not in the hash. A new version that gives another bundle moves `code`.
|
|
31
|
+
* The computer is no part of the stamp. Each record keeps it as a field.
|
|
32
|
+
* The bundler is the optional peer `esbuild`, so a caller that only reads verdicts installs no native binary.
|
|
33
|
+
*/
|
|
34
|
+
/** The words in a bundle that show an input from outside the bundle. A word inside a text also counts, so the scan can only say too much. */
|
|
35
|
+
const CODE_STAMP_READ_WORDS = [
|
|
36
|
+
"readFile",
|
|
37
|
+
"readFileSync",
|
|
38
|
+
"createReadStream",
|
|
39
|
+
"readdir",
|
|
40
|
+
"readdirSync",
|
|
41
|
+
"opendir",
|
|
42
|
+
"fetch",
|
|
43
|
+
"WebAssembly",
|
|
44
|
+
"require",
|
|
45
|
+
"process.env",
|
|
46
|
+
"import.meta.url",
|
|
47
|
+
"import("
|
|
48
|
+
];
|
|
49
|
+
const sha256 = (text) => createHash("sha256").update(text).digest("hex");
|
|
50
|
+
const unquote = (text) => text.trim().replace(/^'(.*)'$/, "$1").replace(/^"(.*)"$/, "$1");
|
|
51
|
+
/** The first colon outside quotes ends a key. For example, the key `'@a/b@1.0.0'` holds no colon outside its quotes. */
|
|
52
|
+
function splitKey(line) {
|
|
53
|
+
const body = line.trim();
|
|
54
|
+
if (body.startsWith("'") || body.startsWith("\"")) {
|
|
55
|
+
const end = body.indexOf(body[0], 1);
|
|
56
|
+
return [body.slice(1, end), body.slice(end + 1).replace(/^:/, "").trim()];
|
|
57
|
+
}
|
|
58
|
+
const colon = body.indexOf(":");
|
|
59
|
+
return colon < 0 ? [body, ""] : [body.slice(0, colon), body.slice(colon + 1).trim()];
|
|
60
|
+
}
|
|
61
|
+
const indentOf = (line) => line.length - line.trimStart().length;
|
|
62
|
+
/** A value in `dependencies` of a snapshot is a version with peers, or a whole key when the name is an alias. */
|
|
63
|
+
function keyOfDependency(name, value) {
|
|
64
|
+
return /^[^\d(].*@\d/.test(value) ? value : `${name}@${value}`;
|
|
65
|
+
}
|
|
66
|
+
/** This function reads one document of a pnpm lock file. It reads only lines that the closure needs. */
|
|
67
|
+
function readLockDocument(lines) {
|
|
68
|
+
const doc = {
|
|
69
|
+
importers: /* @__PURE__ */ new Map(),
|
|
70
|
+
packages: /* @__PURE__ */ new Map(),
|
|
71
|
+
snapshots: /* @__PURE__ */ new Map()
|
|
72
|
+
};
|
|
73
|
+
let section = "";
|
|
74
|
+
let top = "";
|
|
75
|
+
let group = "";
|
|
76
|
+
let name = "";
|
|
77
|
+
for (const line of lines) {
|
|
78
|
+
if (line.trim() === "") continue;
|
|
79
|
+
const indent = indentOf(line);
|
|
80
|
+
const [key, value] = splitKey(line);
|
|
81
|
+
if (indent === 0) {
|
|
82
|
+
section = key;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (indent === 2) {
|
|
86
|
+
top = key;
|
|
87
|
+
group = "";
|
|
88
|
+
if (section === "importers") doc.importers.set(top, /* @__PURE__ */ new Map());
|
|
89
|
+
if (section === "snapshots") doc.snapshots.set(top, []);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (section === "packages" && indent === 4 && key === "resolution") doc.packages.set(top, value);
|
|
93
|
+
if (section === "snapshots") {
|
|
94
|
+
if (indent === 4) group = key;
|
|
95
|
+
else if (indent === 6 && (group === "dependencies" || group === "optionalDependencies")) doc.snapshots.get(top).push(keyOfDependency(key, unquote(value)));
|
|
96
|
+
}
|
|
97
|
+
if (section === "importers") {
|
|
98
|
+
if (indent === 4) group = key;
|
|
99
|
+
else if (indent === 6) name = key;
|
|
100
|
+
else if (indent === 8 && key === "version") doc.importers.get(top).set(name, unquote(value));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return doc;
|
|
104
|
+
}
|
|
105
|
+
const withoutPeers = (key) => key.replace(/\(.*$/, "");
|
|
106
|
+
/**
|
|
107
|
+
* This function gives the closure of each engine from the text of a pnpm lock file of version 9, in the order of `names`.
|
|
108
|
+
* A closure holds each package that the engine pulls in, with its peers, in sorted order.
|
|
109
|
+
* It refuses another lock version, an unknown importer or name, and a workspace package, which belongs in the bundle.
|
|
110
|
+
*/
|
|
111
|
+
function lockClosure(lockText, importer, names) {
|
|
112
|
+
const lines = (lockText.split(/\r?\n---\r?\n|^---\r?\n/).filter((text) => text.includes("importers:")).at(-1) ?? "").split(/\r?\n/);
|
|
113
|
+
const version = lines.find((line) => line.startsWith("lockfileVersion:"));
|
|
114
|
+
if (version === void 0 || !/^9\./.test(unquote(version.slice(16)))) throw new Error(`The lock file has the version line ${version ?? "(none)"}. This reader knows only lock version 9.`);
|
|
115
|
+
const doc = readLockDocument(lines);
|
|
116
|
+
const packages = doc.importers.get(importer);
|
|
117
|
+
if (packages === void 0) throw new Error(`The lock file has no importer ${importer}.`);
|
|
118
|
+
return names.map((name) => {
|
|
119
|
+
const resolved = packages.get(name);
|
|
120
|
+
if (resolved === void 0) throw new Error(`The importer ${importer} has no package ${name} in the lock file.`);
|
|
121
|
+
if (resolved.startsWith("link:")) throw new Error(`The engine ${name} is a workspace package (${resolved}). It has no lock closure, so leave it out of the engines and let it enter the bundle.`);
|
|
122
|
+
const start = keyOfDependency(name, resolved);
|
|
123
|
+
const seen = /* @__PURE__ */ new Set();
|
|
124
|
+
const stack = [start];
|
|
125
|
+
while (stack.length > 0) {
|
|
126
|
+
const key = stack.pop();
|
|
127
|
+
if (seen.has(key)) continue;
|
|
128
|
+
const dependencies = doc.snapshots.get(key);
|
|
129
|
+
if (dependencies === void 0) throw new Error(`The lock file has no snapshot ${key}, which ${name} pulls in.`);
|
|
130
|
+
seen.add(key);
|
|
131
|
+
stack.push(...dependencies);
|
|
132
|
+
}
|
|
133
|
+
const closure = [...seen].sort().map((key) => `${key} ${doc.packages.get(withoutPeers(key)) ?? ""}`);
|
|
134
|
+
return {
|
|
135
|
+
name,
|
|
136
|
+
version: withoutPeers(resolved),
|
|
137
|
+
closure
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async function loadBundler() {
|
|
142
|
+
try {
|
|
143
|
+
return await import("esbuild");
|
|
144
|
+
} catch (cause) {
|
|
145
|
+
throw new Error("The function codeStamp uses the optional peer esbuild. Add esbuild to the dependencies of the caller, so that its lock file pins the version.", { cause });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const forwardSlashes = (path) => path.split("\\").join("/");
|
|
149
|
+
const fromCwd = (cwd, path) => forwardSlashes(relative(cwd, resolve(cwd, forwardSlashes(path))));
|
|
150
|
+
/** This function gives the bytes that a data file hashes as. Git uses the same test for a binary file: a NUL byte. */
|
|
151
|
+
function dataBytes(bytes) {
|
|
152
|
+
return bytes.includes(0) ? bytes : Buffer.from(bytes.toString("latin1").replaceAll("\r\n", "\n"), "latin1");
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* This function gives the stamp of the code of a run. Only the options, the source files and the data files decide it, so another folder gives the same stamp.
|
|
156
|
+
* A change in comments, blank space, line ends or types alone keeps the stamp. A new export that the entries do not reach also keeps it.
|
|
157
|
+
* A change in a token that the entries reach moves `parts.code`. Identifiers stay as they are, so a rename also moves it. This gives a false rerun, not a false keep.
|
|
158
|
+
* A bundle cannot see a file that the code reads when it runs. So a word of `CODE_STAMP_READ_WORDS` that `reads` does not state refuses the stamp, and `dataFiles` lists the files.
|
|
159
|
+
*/
|
|
160
|
+
async function codeStamp(options) {
|
|
161
|
+
const cwd = resolve(options.cwd);
|
|
162
|
+
const entries = [...new Set(options.entries.map((entry) => fromCwd(cwd, entry)))].sort();
|
|
163
|
+
if (entries.length === 0) throw new Error("A code stamp needs at least one entry.");
|
|
164
|
+
const outside = entries.find((entry) => entry.startsWith("..") || isAbsolute(entry));
|
|
165
|
+
if (outside !== void 0) throw new Error(`The entry ${outside} is outside ${cwd}. Another folder would give another stamp.`);
|
|
166
|
+
const bundler = await loadBundler();
|
|
167
|
+
const result = await bundler.build({
|
|
168
|
+
stdin: {
|
|
169
|
+
contents: entries.map((entry, i) => `export * as e${i} from "./${entry}";`).join("\n"),
|
|
170
|
+
resolveDir: cwd,
|
|
171
|
+
loader: "ts"
|
|
172
|
+
},
|
|
173
|
+
absWorkingDir: cwd,
|
|
174
|
+
bundle: true,
|
|
175
|
+
format: "esm",
|
|
176
|
+
platform: "node",
|
|
177
|
+
write: false,
|
|
178
|
+
outfile: "stamp.js",
|
|
179
|
+
treeShaking: true,
|
|
180
|
+
minifyWhitespace: true,
|
|
181
|
+
legalComments: "none",
|
|
182
|
+
metafile: true,
|
|
183
|
+
logLevel: "silent",
|
|
184
|
+
tsconfigRaw: {},
|
|
185
|
+
external: [...options.engines?.names ?? []]
|
|
186
|
+
});
|
|
187
|
+
const text = (result.outputFiles ?? []).map((file) => file.text).join("\n");
|
|
188
|
+
const reads = CODE_STAMP_READ_WORDS.filter((word) => hasWord(text, word));
|
|
189
|
+
const stated = [...new Set(options.reads ?? [])];
|
|
190
|
+
const notStated = reads.filter((word) => !stated.includes(word));
|
|
191
|
+
const notFound = stated.filter((word) => !reads.includes(word));
|
|
192
|
+
if (notStated.length > 0 || notFound.length > 0) throw new Error(`The reads of the bundle differ from the option reads. In the bundle and not stated: ${notStated.join(", ") || "(none)"}. Stated and not in the bundle: ${notFound.join(", ") || "(none)"}. List each file that such a read opens in dataFiles.`);
|
|
193
|
+
const inputs = /* @__PURE__ */ new Set();
|
|
194
|
+
for (const output of Object.values(result.metafile?.outputs ?? {})) for (const [input, facts] of Object.entries(output.inputs)) if (facts.bytesInOutput > 0 && input !== "<stdin>") inputs.add(forwardSlashes(input));
|
|
195
|
+
const engines = options.engines === void 0 ? [] : lockClosure(readFileSync(resolve(cwd, options.engines.lockFile), "utf8"), options.engines.importer, options.engines.names);
|
|
196
|
+
const data = [...new Set((options.dataFiles ?? []).map((file) => fromCwd(cwd, file)))].sort().map((file) => `${file} ${sha256(dataBytes(readFileSync(resolve(cwd, file))))}`);
|
|
197
|
+
const parts = {
|
|
198
|
+
code: sha256(text),
|
|
199
|
+
engines: sha256(JSON.stringify(engines)),
|
|
200
|
+
data: sha256(JSON.stringify(data)),
|
|
201
|
+
bundler: `esbuild@${bundler.version}`,
|
|
202
|
+
node: options.node ?? process.versions.node.split(".")[0]
|
|
203
|
+
};
|
|
204
|
+
const { bundler: _version, ...hashed } = parts;
|
|
205
|
+
return {
|
|
206
|
+
hash: sha256(JSON.stringify(hashed)),
|
|
207
|
+
parts,
|
|
208
|
+
inputs: [...inputs].sort(),
|
|
209
|
+
reads,
|
|
210
|
+
engines
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
export { CODE_STAMP_READ_WORDS, codeStamp, lockClosure };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=code-stamp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-stamp.js","names":[],"sources":["../src/read-words.ts","../src/code-stamp.ts"],"sourcesContent":["/** The test of a word against the text of a bundle. This file is internal, so its name is not in the public API. */\n\nconst isWordCharacter = (character: string | undefined): boolean => character !== undefined && /\\w/.test(character);\n\n/**\n * This function tells if a text holds a word. Each character of the word is literal, so no character of the word needs an escape.\n * A word edge that is a word character must also be a word boundary in the text.\n * So `fetch` is not in `fetcher`, and `import(` is not in `reimport(`.\n * An empty word is in no text.\n */\nexport function hasWord(text: string, word: string): boolean {\n if (word === \"\") return false;\n const startEdge = isWordCharacter(word[0]);\n const endEdge = isWordCharacter(word.at(-1));\n for (let at = text.indexOf(word); at !== -1; at = text.indexOf(word, at + 1)) {\n const startOk = !startEdge || !isWordCharacter(text[at - 1]);\n const endOk = !endEdge || !isWordCharacter(text[at + word.length]);\n if (startOk && endOk) return true;\n }\n return false;\n}\n","/**\n * The stamp of the code of a run, for Node only. A run keeps a saved record when the stamp of the record equals the stamp of the run.\n * A version number is a poor stamp. A release that changes no result throws away each record. A change that has no release keeps wrong records.\n * This entry hashes what can change a result. These are the code that the entries reach after a tree shake and the lock closure of the engines.\n * The stated data files and the Node major are also in the hash.\n * The bundler only makes the text that the hash reads, and no run uses that text. So its version is a part of the stamp and is not in the hash. A new version that gives another bundle moves `code`.\n * The computer is no part of the stamp. Each record keeps it as a field.\n * The bundler is the optional peer `esbuild`, so a caller that only reads verdicts installs no native binary.\n */\nimport { createHash } from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport { hasWord } from \"./read-words.ts\";\n\n/** The engines of a run, which stay outside the bundle. The lock file gives what each engine pulls in. */\nexport interface CodeStampEngines {\n /** The package names. A subpath of a name also stays outside the bundle. */\n readonly names: readonly string[];\n /** The path of the `pnpm-lock.yaml`, from `cwd`. */\n readonly lockFile: string;\n /** The key of the package in `importers` of the lock file, for example `packages/joint`. */\n readonly importer: string;\n}\n\n/** The inputs of `codeStamp`. Each relative path starts at `cwd`. */\nexport interface CodeStampOptions {\n readonly cwd: string;\n /** The source files that a run starts from. Each export of these files stays in the bundle, so a new export of an entry moves the stamp. */\n readonly entries: readonly string[];\n readonly engines?: CodeStampEngines;\n /**\n * The files that the code reads when it runs, for example a corpus or a table. A bundle cannot see them, so each has its own hash.\n * The hash of a text file reads each line end as LF, so a checkout with CRLF gives the same stamp. A file with a NUL byte hashes as its bytes.\n */\n readonly dataFiles?: readonly string[];\n /** The words of `CODE_STAMP_READ_WORDS` that the caller expects in the bundle. A difference in either direction refuses the stamp. */\n readonly reads?: readonly string[];\n /** The Node major of the stamp. The default is the major of this process. */\n readonly node?: string;\n}\n\n/** One engine of a stamp: the version in the lock file, and each package of its closure with its peers. */\nexport interface EngineClosure {\n readonly name: string;\n readonly version: string;\n readonly closure: readonly string[];\n}\n\n/** The hash of each part of a stamp, so that a change of the stamp names its cause. */\nexport interface CodeStampParts {\n readonly code: string;\n readonly engines: string;\n readonly data: string;\n /** The name and the version of the bundler, for example `esbuild@0.28.2`. It tells the cause of a change of `code`, and it is not in the hash. */\n readonly bundler: string;\n readonly node: string;\n}\n\n/** The result of `codeStamp`. Only `hash` and `parts` are the stamp. The other fields tell what went in. */\nexport interface CodeStamp {\n readonly hash: string;\n readonly parts: CodeStampParts;\n /** The source files that put at least one byte in the bundle, with paths from `cwd`. */\n readonly inputs: readonly string[];\n /** The words of `CODE_STAMP_READ_WORDS` in the bundle. */\n readonly reads: readonly string[];\n readonly engines: readonly EngineClosure[];\n}\n\n/** The words in a bundle that show an input from outside the bundle. A word inside a text also counts, so the scan can only say too much. */\nexport const CODE_STAMP_READ_WORDS: readonly string[] = [\n \"readFile\",\n \"readFileSync\",\n \"createReadStream\",\n \"readdir\",\n \"readdirSync\",\n \"opendir\",\n \"fetch\",\n \"WebAssembly\",\n \"require\",\n \"process.env\",\n \"import.meta.url\",\n \"import(\",\n];\n\nconst sha256 = (text: string | Uint8Array): string => createHash(\"sha256\").update(text).digest(\"hex\");\n\n/** A lock document, read to the three maps that a closure needs. */\ninterface LockDocument {\n /** The key of each importer, then the name and the version of each of its packages. */\n readonly importers: Map<string, Map<string, string>>;\n /** The resolution line of each package, with no peers in the key. */\n readonly packages: Map<string, string>;\n /** The key of each snapshot, with the keys of its dependencies. */\n readonly snapshots: Map<string, string[]>;\n}\n\nconst unquote = (text: string): string => text.trim().replace(/^'(.*)'$/, \"$1\").replace(/^\"(.*)\"$/, \"$1\");\n\n/** The first colon outside quotes ends a key. For example, the key `'@a/b@1.0.0'` holds no colon outside its quotes. */\nfunction splitKey(line: string): readonly [string, string] {\n const body = line.trim();\n if (body.startsWith(\"'\") || body.startsWith('\"')) {\n const end = body.indexOf(body[0]!, 1);\n return [body.slice(1, end), body.slice(end + 1).replace(/^:/, \"\").trim()];\n }\n const colon = body.indexOf(\":\");\n return colon < 0 ? [body, \"\"] : [body.slice(0, colon), body.slice(colon + 1).trim()];\n}\n\nconst indentOf = (line: string): number => line.length - line.trimStart().length;\n\n/** A value in `dependencies` of a snapshot is a version with peers, or a whole key when the name is an alias. */\nfunction keyOfDependency(name: string, value: string): string {\n return /^[^\\d(].*@\\d/.test(value) ? value : `${name}@${value}`;\n}\n\n/** This function reads one document of a pnpm lock file. It reads only lines that the closure needs. */\nfunction readLockDocument(lines: readonly string[]): LockDocument {\n const doc: LockDocument = { importers: new Map(), packages: new Map(), snapshots: new Map() };\n let section = \"\";\n let top = \"\";\n let group = \"\";\n let name = \"\";\n for (const line of lines) {\n if (line.trim() === \"\") continue;\n const indent = indentOf(line);\n const [key, value] = splitKey(line);\n if (indent === 0) {\n section = key;\n continue;\n }\n if (indent === 2) {\n top = key;\n group = \"\";\n if (section === \"importers\") doc.importers.set(top, new Map());\n if (section === \"snapshots\") doc.snapshots.set(top, []);\n continue;\n }\n if (section === \"packages\" && indent === 4 && key === \"resolution\") doc.packages.set(top, value);\n if (section === \"snapshots\") {\n if (indent === 4) group = key;\n else if (indent === 6 && (group === \"dependencies\" || group === \"optionalDependencies\")) doc.snapshots.get(top)!.push(keyOfDependency(key, unquote(value)));\n }\n if (section === \"importers\") {\n if (indent === 4) group = key;\n else if (indent === 6) name = key;\n else if (indent === 8 && key === \"version\") doc.importers.get(top)!.set(name, unquote(value));\n }\n }\n return doc;\n}\n\nconst withoutPeers = (key: string): string => key.replace(/\\(.*$/, \"\");\n\n/**\n * This function gives the closure of each engine from the text of a pnpm lock file of version 9, in the order of `names`.\n * A closure holds each package that the engine pulls in, with its peers, in sorted order.\n * It refuses another lock version, an unknown importer or name, and a workspace package, which belongs in the bundle.\n */\nexport function lockClosure(lockText: string, importer: string, names: readonly string[]): readonly EngineClosure[] {\n const documents = lockText.split(/\\r?\\n---\\r?\\n|^---\\r?\\n/).filter((text) => text.includes(\"importers:\"));\n const lines = (documents.at(-1) ?? \"\").split(/\\r?\\n/);\n const version = lines.find((line) => line.startsWith(\"lockfileVersion:\"));\n if (version === undefined || !/^9\\./.test(unquote(version.slice(\"lockfileVersion:\".length)))) throw new Error(`The lock file has the version line ${version ?? \"(none)\"}. This reader knows only lock version 9.`);\n const doc = readLockDocument(lines);\n const packages = doc.importers.get(importer);\n if (packages === undefined) throw new Error(`The lock file has no importer ${importer}.`);\n return names.map((name) => {\n const resolved = packages.get(name);\n if (resolved === undefined) throw new Error(`The importer ${importer} has no package ${name} in the lock file.`);\n if (resolved.startsWith(\"link:\")) throw new Error(`The engine ${name} is a workspace package (${resolved}). It has no lock closure, so leave it out of the engines and let it enter the bundle.`);\n const start = keyOfDependency(name, resolved);\n const seen = new Set<string>();\n const stack = [start];\n while (stack.length > 0) {\n const key = stack.pop()!;\n if (seen.has(key)) continue;\n const dependencies = doc.snapshots.get(key);\n if (dependencies === undefined) throw new Error(`The lock file has no snapshot ${key}, which ${name} pulls in.`);\n seen.add(key);\n stack.push(...dependencies);\n }\n const closure = [...seen].sort().map((key) => `${key} ${doc.packages.get(withoutPeers(key)) ?? \"\"}`);\n return { name, version: withoutPeers(resolved), closure };\n });\n}\n\n/** The part of a bundler that this entry uses. */\ninterface Bundler {\n readonly version: string;\n build(options: Record<string, unknown>): Promise<{\n readonly outputFiles?: readonly { readonly text: string }[];\n readonly metafile?: { readonly outputs: Record<string, { readonly inputs: Record<string, { readonly bytesInOutput: number }> }> };\n }>;\n}\n\nasync function loadBundler(): Promise<Bundler> {\n try {\n return (await import(\"esbuild\")) as unknown as Bundler;\n } catch (cause) {\n throw new Error(\"The function codeStamp uses the optional peer esbuild. Add esbuild to the dependencies of the caller, so that its lock file pins the version.\", { cause });\n }\n}\n\nconst forwardSlashes = (path: string): string => path.split(\"\\\\\").join(\"/\");\n\nconst fromCwd = (cwd: string, path: string): string => forwardSlashes(relative(cwd, resolve(cwd, forwardSlashes(path))));\n\n/** This function gives the bytes that a data file hashes as. Git uses the same test for a binary file: a NUL byte. */\nfunction dataBytes(bytes: Buffer): Buffer {\n return bytes.includes(0) ? bytes : Buffer.from(bytes.toString(\"latin1\").replaceAll(\"\\r\\n\", \"\\n\"), \"latin1\");\n}\n\n/**\n * This function gives the stamp of the code of a run. Only the options, the source files and the data files decide it, so another folder gives the same stamp.\n * A change in comments, blank space, line ends or types alone keeps the stamp. A new export that the entries do not reach also keeps it.\n * A change in a token that the entries reach moves `parts.code`. Identifiers stay as they are, so a rename also moves it. This gives a false rerun, not a false keep.\n * A bundle cannot see a file that the code reads when it runs. So a word of `CODE_STAMP_READ_WORDS` that `reads` does not state refuses the stamp, and `dataFiles` lists the files.\n */\nexport async function codeStamp(options: CodeStampOptions): Promise<CodeStamp> {\n const cwd = resolve(options.cwd);\n const entries = [...new Set(options.entries.map((entry) => fromCwd(cwd, entry)))].sort();\n if (entries.length === 0) throw new Error(\"A code stamp needs at least one entry.\");\n const outside = entries.find((entry) => entry.startsWith(\"..\") || isAbsolute(entry));\n if (outside !== undefined) throw new Error(`The entry ${outside} is outside ${cwd}. Another folder would give another stamp.`);\n const bundler = await loadBundler();\n const result = await bundler.build({\n stdin: { contents: entries.map((entry, i) => `export * as e${i} from \"./${entry}\";`).join(\"\\n\"), resolveDir: cwd, loader: \"ts\" },\n absWorkingDir: cwd,\n bundle: true,\n format: \"esm\",\n platform: \"node\",\n write: false,\n outfile: \"stamp.js\",\n treeShaking: true,\n minifyWhitespace: true,\n legalComments: \"none\",\n metafile: true,\n logLevel: \"silent\",\n // The tsconfig file of a package is not an input: another compiler option in that file does not move the stamp.\n tsconfigRaw: {},\n external: [...(options.engines?.names ?? [])],\n });\n const text = (result.outputFiles ?? []).map((file) => file.text).join(\"\\n\");\n const reads = CODE_STAMP_READ_WORDS.filter((word) => hasWord(text, word));\n const stated = [...new Set(options.reads ?? [])];\n const notStated = reads.filter((word) => !stated.includes(word));\n const notFound = stated.filter((word) => !reads.includes(word));\n if (notStated.length > 0 || notFound.length > 0) {\n throw new Error(\n `The reads of the bundle differ from the option reads. In the bundle and not stated: ${notStated.join(\", \") || \"(none)\"}. Stated and not in the bundle: ${notFound.join(\", \") || \"(none)\"}. List each file that such a read opens in dataFiles.`,\n );\n }\n const inputs = new Set<string>();\n for (const output of Object.values(result.metafile?.outputs ?? {})) {\n for (const [input, facts] of Object.entries(output.inputs)) if (facts.bytesInOutput > 0 && input !== \"<stdin>\") inputs.add(forwardSlashes(input));\n }\n const engines = options.engines === undefined ? [] : lockClosure(readFileSync(resolve(cwd, options.engines.lockFile), \"utf8\"), options.engines.importer, options.engines.names);\n const data = [...new Set((options.dataFiles ?? []).map((file) => fromCwd(cwd, file)))].sort().map((file) => `${file} ${sha256(dataBytes(readFileSync(resolve(cwd, file))))}`);\n const parts: CodeStampParts = {\n code: sha256(text),\n engines: sha256(JSON.stringify(engines)),\n data: sha256(JSON.stringify(data)),\n bundler: `esbuild@${bundler.version}`,\n node: options.node ?? process.versions.node.split(\".\")[0]!,\n };\n const { bundler: _version, ...hashed } = parts;\n return { hash: sha256(JSON.stringify(hashed)), parts, inputs: [...inputs].sort(), reads, engines };\n}\n"],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -1335,6 +1335,71 @@ interface Power {
|
|
|
1335
1335
|
* Each arm of each round adds a normal noise of `sigma` to the log time.
|
|
1336
1336
|
*/
|
|
1337
1337
|
export declare function verdictPower(options: PowerOptions): Power;
|
|
1338
|
+
/**
|
|
1339
|
+
* The rows that accept a changed result of a bench case. A bench compares two arms, and two arms with different results do not do the same work.
|
|
1340
|
+
* So a difference stops the merge. A pull request can change a result on purpose, and without a stated path its author edits the case, and no gate reads that edit.
|
|
1341
|
+
* A row is that stated path. It names the case and the hash of each result, and it gives a reason. A reviewer can read the row against the run.
|
|
1342
|
+
*
|
|
1343
|
+
* Three rules stop the reuse of a row. The fields `base` and `head` bind the row to one change in one direction. A row counts only if it is new: the rows of the base are spent.
|
|
1344
|
+
* A new row that accepts nothing is a fault. These rules read results only, so the time of an accepted case stays with the time verdict of the bench.
|
|
1345
|
+
*/
|
|
1346
|
+
/**
|
|
1347
|
+
* One row: a statement that the result of one case changes on purpose.
|
|
1348
|
+
* The caller uses one hash function for the row and for the run. Two hash functions give a refused line on each row, and each line reads "both sides moved".
|
|
1349
|
+
*/
|
|
1350
|
+
interface ResultRow {
|
|
1351
|
+
readonly case: string;
|
|
1352
|
+
/** The hash of the result of the base arm. The caller makes the hash, so a large result stays out of the file of rows. */
|
|
1353
|
+
readonly base: string;
|
|
1354
|
+
/** The hash of the result of the head arm. */
|
|
1355
|
+
readonly head: string;
|
|
1356
|
+
/** The reason for the change, in plain words. The verdict prints it. */
|
|
1357
|
+
readonly reason: string;
|
|
1358
|
+
}
|
|
1359
|
+
/** The hash of the result of one case in each arm. The hashes come from the same function as the hashes of the rows. */
|
|
1360
|
+
interface CaseResults {
|
|
1361
|
+
readonly case: string;
|
|
1362
|
+
readonly base: string;
|
|
1363
|
+
readonly head: string;
|
|
1364
|
+
}
|
|
1365
|
+
/** The rows of the base and of the head. A row of the head that the base also has is spent. */
|
|
1366
|
+
interface ResultRows {
|
|
1367
|
+
readonly base: readonly ResultRow[];
|
|
1368
|
+
readonly head: readonly ResultRow[];
|
|
1369
|
+
}
|
|
1370
|
+
/** The read of one case with two different results. */
|
|
1371
|
+
interface ResultRead extends CaseResults {
|
|
1372
|
+
readonly outcome: "accepted" | "refused";
|
|
1373
|
+
/** The line that the check prints. A refused line gives the path through. */
|
|
1374
|
+
readonly line: string;
|
|
1375
|
+
}
|
|
1376
|
+
/** The read of one new row that accepted nothing. */
|
|
1377
|
+
interface RowRead {
|
|
1378
|
+
readonly row: ResultRow;
|
|
1379
|
+
readonly line: string;
|
|
1380
|
+
}
|
|
1381
|
+
/** A verdict over the results of a run and the rows. */
|
|
1382
|
+
interface ResultVerdict {
|
|
1383
|
+
/** The value "fail" means a refused case, or a new row that accepted nothing. */
|
|
1384
|
+
readonly outcome: "pass" | "fail";
|
|
1385
|
+
/** The cases with two different results. */
|
|
1386
|
+
readonly cases: readonly ResultRead[];
|
|
1387
|
+
/** The new rows of a case with one result, or with another change. Each one fails the verdict. */
|
|
1388
|
+
readonly unused: readonly RowRead[];
|
|
1389
|
+
/** The new rows of a case with no result in this run. The case can be skipped or filtered out. A missing read is no pass, so each one fails the verdict. */
|
|
1390
|
+
readonly absent: readonly RowRead[];
|
|
1391
|
+
/** The rows of the base that the head keeps. They accept nothing, and they do not fail the verdict. */
|
|
1392
|
+
readonly spent: readonly ResultRow[];
|
|
1393
|
+
/** The lines of `cases`, `unused` and `absent`, then one line with the count of `spent`. */
|
|
1394
|
+
readonly lines: readonly string[];
|
|
1395
|
+
}
|
|
1396
|
+
/** This function checks the parsed content of a file of rows. A row with an empty field, or the same row two times, stops the read with an error. */
|
|
1397
|
+
export declare function resultRows(content: unknown): ResultRow[];
|
|
1398
|
+
/**
|
|
1399
|
+
* This function gives the verdict over the results of a run. A case with two different results passes only with a new row of its exact hashes.
|
|
1400
|
+
* The verdict reads results only, so an accepted case still needs its time verdict.
|
|
1401
|
+
*/
|
|
1402
|
+
export declare function resultVerdict(results: readonly CaseResults[], rows: ResultRows): ResultVerdict;
|
|
1338
1403
|
/** The options of the timed rounds of one case. */
|
|
1339
1404
|
interface RoundsOptions {
|
|
1340
1405
|
/** The name of the case. */
|
|
@@ -1364,5 +1429,5 @@ interface Rounds {
|
|
|
1364
1429
|
* keeps the work.
|
|
1365
1430
|
*/
|
|
1366
1431
|
export declare function timeRounds(arms: Readonly<Record<Arm, () => number>>, options: RoundsOptions): Rounds;
|
|
1367
|
-
export { type Arm, type ArmModels, type ArmPair, type Balanced, type Band, type BandKind, type BandOptions, type BudgetLook, type BudgetLooksOptions, type BudgetLooksVerdict, type BudgetMore, type BudgetOptions, type BudgetOutcome, type BudgetResult, type BudgetRound, type BudgetVerdict, type CaseBudget, type CaseGate, type CaseInterval, type CaseLimit, type CaseRatio, type CaseTail, type CaseTailLimit, type Comparison, type ControlFactors, type ControlOptions, type ControlRead, type ControlRow, type EntryCount, type Gate, type GateCaseOutcome, type GateJobRead, type GateOutcome, type GroupCounts, type GroupFactor, type GuardRead, type JobLimitVerdict, type JobRecords, type JobVerdict, type JointClaim, type JointControl, type JointMeasure, type JointVerdict, type Ladder, type LadderDimension, type LadderOptions, type LadderVerdict, type LimitOnModel, type LimitOptions, type LimitOutcome, type LimitVerdict, type MeasureCheck, type MeasureCheckOptions, type MeasureChecks, type MeasureFinding, type MeasureFindingKind, type MeasureRow, type MeasureRule, type MeasuredFactor, type MedianBudget, type ModelLimits, type ModelRead, type ModelRow, type ModelStatus, type ModelTest, type ModelTestOptions, type NoControl, type NoFactor, type NoiseCheck, type NotSlowerOptions, type NotSlowerRead, type NotSlowerVerdict, type OrderSize, type PairedTime, type PartnerRead, type PoolFacts, type PoolOptions, type PooledJob, type PooledLimitVerdict, type PooledRecords, type PooledVerdict, type Power, type PowerOptions, type QuantileBudget, type RateBudget, type Rounds, type RoundsOptions, type RunPairs, type Rung, type RungBudget, type RungEvidence, type RungRecord, type RungVerdict, type SpreadRead, type TailFalseFails, type TailGainCase, type TailLimit, type TailLimitOptions, type TailLimitVerdict, type TailRuleOptions, type TimeOutcome, type TimeVerdict, type VerdictOptions, type VerdictRule, type VisibleShift, type WinClaim, type WorstBudget, armOrder, caseRatios, checkPairedTime, firstArm, orderSize };
|
|
1432
|
+
export { type Arm, type ArmModels, type ArmPair, type Balanced, type Band, type BandKind, type BandOptions, type BudgetLook, type BudgetLooksOptions, type BudgetLooksVerdict, type BudgetMore, type BudgetOptions, type BudgetOutcome, type BudgetResult, type BudgetRound, type BudgetVerdict, type CaseBudget, type CaseGate, type CaseInterval, type CaseLimit, type CaseRatio, type CaseResults, type CaseTail, type CaseTailLimit, type Comparison, type ControlFactors, type ControlOptions, type ControlRead, type ControlRow, type EntryCount, type Gate, type GateCaseOutcome, type GateJobRead, type GateOutcome, type GroupCounts, type GroupFactor, type GuardRead, type JobLimitVerdict, type JobRecords, type JobVerdict, type JointClaim, type JointControl, type JointMeasure, type JointVerdict, type Ladder, type LadderDimension, type LadderOptions, type LadderVerdict, type LimitOnModel, type LimitOptions, type LimitOutcome, type LimitVerdict, type MeasureCheck, type MeasureCheckOptions, type MeasureChecks, type MeasureFinding, type MeasureFindingKind, type MeasureRow, type MeasureRule, type MeasuredFactor, type MedianBudget, type ModelLimits, type ModelRead, type ModelRow, type ModelStatus, type ModelTest, type ModelTestOptions, type NoControl, type NoFactor, type NoiseCheck, type NotSlowerOptions, type NotSlowerRead, type NotSlowerVerdict, type OrderSize, type PairedTime, type PartnerRead, type PoolFacts, type PoolOptions, type PooledJob, type PooledLimitVerdict, type PooledRecords, type PooledVerdict, type Power, type PowerOptions, type QuantileBudget, type RateBudget, type ResultRead, type ResultRow, type ResultRows, type ResultVerdict, type Rounds, type RoundsOptions, type RowRead, type RunPairs, type Rung, type RungBudget, type RungEvidence, type RungRecord, type RungVerdict, type SpreadRead, type TailFalseFails, type TailGainCase, type TailLimit, type TailLimitOptions, type TailLimitVerdict, type TailRuleOptions, type TimeOutcome, type TimeVerdict, type VerdictOptions, type VerdictRule, type VisibleShift, type WinClaim, type WorstBudget, armOrder, caseRatios, checkPairedTime, firstArm, orderSize };
|
|
1368
1433
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":["Comparison"],"sources":["../src/budget.ts","../src/verdict.ts","../src/limit.ts","../src/merge.ts","../src/gate.ts","../src/control.ts","../src/ladder.ts","../src/model.ts","../src/tail.ts","../src/measures.ts","../src/power.ts","../src/rounds.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["Comparison"],"sources":["../src/budget.ts","../src/verdict.ts","../src/limit.ts","../src/merge.ts","../src/gate.ts","../src/control.ts","../src/ladder.ts","../src/model.ts","../src/tail.ts","../src/measures.ts","../src/power.ts","../src/results.ts","../src/rounds.ts"],"mappings":"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"}
|