@mikeargento/bitgraph-player 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/SPEC.md +5 -2
- package/dist/__tests__/check.test.d.ts +2 -0
- package/dist/__tests__/check.test.d.ts.map +1 -0
- package/dist/__tests__/check.test.js +389 -0
- package/dist/__tests__/check.test.js.map +1 -0
- package/dist/check.d.ts +156 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +640 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.js +84 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/verdict.d.ts +1 -1
- package/dist/verdict.js +1 -1
- package/dist-web/verify.html +159 -0
- package/package.json +10 -4
- package/src/__tests__/check.test.ts +451 -0
- package/src/__tests__/fixtures/declared-12010/proof.json +1 -0
- package/src/__tests__/fixtures/export-random-043/ethereum-anchors/anchor-after-witness.json +6 -0
- package/src/__tests__/fixtures/export-random-043/ethereum-anchors/anchor-after.json +43 -0
- package/src/__tests__/fixtures/export-random-043/ethereum-anchors/anchor-before-witness.json +6 -0
- package/src/__tests__/fixtures/export-random-043/ethereum-anchors/anchor-before.json +47 -0
- package/src/__tests__/fixtures/export-random-043/proof.json +38 -0
- package/src/__tests__/fixtures/export-random-043/random-043.txt +1 -0
- package/src/check.ts +873 -0
- package/src/cli.ts +82 -16
- package/src/index.ts +3 -0
- package/src/verdict.ts +1 -1
package/src/cli.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* bitgraph-play <rule.json> <bundle> [--out <file>] [--summary]
|
|
6
6
|
* bitgraph-play init <file>... [--out <rule.json>]
|
|
7
|
+
* bitgraph-play check <bundle-or-file>... [--json] [--out <file>]
|
|
7
8
|
*
|
|
8
9
|
* Evaluate: runs a bitgraph-player/1 rule against a proof bundle
|
|
9
10
|
* (directory, .tar, .tar.gz, or .tgz) and writes the verdict JSON to
|
|
@@ -15,9 +16,15 @@
|
|
|
15
16
|
* a placeholder the author must replace — the trust floor has no
|
|
16
17
|
* default, and the scaffolder must not choose it either.
|
|
17
18
|
*
|
|
19
|
+
* Check: reads a bundle (a directory or archive, or a list of files such
|
|
20
|
+
* as a proof.json and the file it records) and reports, in the three
|
|
21
|
+
* values, what the bundle establishes about each recording it holds and
|
|
22
|
+
* what bounds it. Human text on stdout by default; --json for the
|
|
23
|
+
* bitgraph-check/1 report. Offline. See check.ts for the vocabulary.
|
|
24
|
+
*
|
|
18
25
|
* Exit codes: 0 TRUE, 1 FALSE, 2 UNDETERMINED, 3 error (init: 0 or 3).
|
|
19
|
-
* Diagnostics go to stderr; stdout carries the verdict (or skeleton
|
|
20
|
-
* bytes only.
|
|
26
|
+
* Diagnostics go to stderr; stdout carries the verdict (or skeleton, or
|
|
27
|
+
* check report) bytes only.
|
|
21
28
|
*
|
|
22
29
|
* The process exits by setting process.exitCode and returning, never by
|
|
23
30
|
* process.exit(): exiting early would truncate output still draining to
|
|
@@ -26,9 +33,12 @@
|
|
|
26
33
|
|
|
27
34
|
import { createHash } from "node:crypto";
|
|
28
35
|
import { createReadStream, existsSync, writeFileSync } from "node:fs";
|
|
36
|
+
import { readFile, stat } from "node:fs/promises";
|
|
29
37
|
import { basename } from "node:path";
|
|
30
38
|
import { pipeline } from "node:stream/promises";
|
|
31
|
-
import type { AuditResult } from "@mikeargento/bitgraph-audit";
|
|
39
|
+
import type { AuditResult, BundleEntrySource } from "@mikeargento/bitgraph-audit";
|
|
40
|
+
import { ingestBundle, ingestEntries } from "@mikeargento/bitgraph-audit";
|
|
41
|
+
import { checkIngest, renderCheckText, serializeCheckReport } from "./check.js";
|
|
32
42
|
import { scaffoldRule } from "./init.js";
|
|
33
43
|
import type { ScaffoldEntry } from "./init.js";
|
|
34
44
|
import { play, PlayError } from "./play.js";
|
|
@@ -38,8 +48,9 @@ function usage(): number {
|
|
|
38
48
|
process.stderr.write(
|
|
39
49
|
"usage: bitgraph-play <rule.json> <bundle> [--out <file>] [--summary]\n" +
|
|
40
50
|
" bitgraph-play init <file>... [--out <rule.json>]\n" +
|
|
41
|
-
|
|
42
|
-
"
|
|
51
|
+
" bitgraph-play check <bundle-or-file>... [--json] [--out <file>]\n" +
|
|
52
|
+
' "--" ends option parsing; a rule file literally named "init" or\n' +
|
|
53
|
+
' "check" is evaluated with: bitgraph-play -- init <bundle>\n' +
|
|
43
54
|
" exit codes: 0 TRUE, 1 FALSE, 2 UNDETERMINED, 3 error\n"
|
|
44
55
|
);
|
|
45
56
|
return 3;
|
|
@@ -79,10 +90,11 @@ interface ParsedArgs {
|
|
|
79
90
|
positional: string[];
|
|
80
91
|
outFile?: string;
|
|
81
92
|
summary: boolean;
|
|
93
|
+
json: boolean;
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
function parseArgs(args: string[]): ParsedArgs | undefined {
|
|
85
|
-
const parsed: ParsedArgs = { positional: [], summary: false };
|
|
97
|
+
const parsed: ParsedArgs = { positional: [], summary: false, json: false };
|
|
86
98
|
for (let i = 0; i < args.length; i++) {
|
|
87
99
|
const arg = args[i] as string;
|
|
88
100
|
if (arg === "--") {
|
|
@@ -95,6 +107,8 @@ function parseArgs(args: string[]): ParsedArgs | undefined {
|
|
|
95
107
|
parsed.outFile = next;
|
|
96
108
|
} else if (arg === "--summary") {
|
|
97
109
|
parsed.summary = true;
|
|
110
|
+
} else if (arg === "--json") {
|
|
111
|
+
parsed.json = true;
|
|
98
112
|
} else if (arg.startsWith("-")) {
|
|
99
113
|
return undefined;
|
|
100
114
|
} else {
|
|
@@ -104,8 +118,58 @@ function parseArgs(args: string[]): ParsedArgs | undefined {
|
|
|
104
118
|
return parsed;
|
|
105
119
|
}
|
|
106
120
|
|
|
107
|
-
|
|
121
|
+
/**
|
|
122
|
+
* check: one directory or archive path is ingested as a bundle; any other
|
|
123
|
+
* argument list (one or more plain files, or several paths) is ingested
|
|
124
|
+
* in memory as a flat set of entries named by their basenames, so
|
|
125
|
+
* `bitgraph-play check proof.json photo.jpg` works without a folder.
|
|
126
|
+
*/
|
|
127
|
+
async function runCheck(args: ParsedArgs): Promise<number> {
|
|
108
128
|
if (args.summary) return usage();
|
|
129
|
+
const targets = args.positional;
|
|
130
|
+
if (targets.length === 0) return usage();
|
|
131
|
+
|
|
132
|
+
let ingest;
|
|
133
|
+
try {
|
|
134
|
+
const single = targets.length === 1 ? await stat(targets[0] as string) : undefined;
|
|
135
|
+
if (single !== undefined && (single.isDirectory() || looksLikeArchive(targets[0] as string))) {
|
|
136
|
+
ingest = await ingestBundle(targets[0] as string);
|
|
137
|
+
} else {
|
|
138
|
+
const entries: BundleEntrySource[] = [];
|
|
139
|
+
for (const target of targets) {
|
|
140
|
+
const info = await stat(target);
|
|
141
|
+
if (info.isDirectory()) {
|
|
142
|
+
// A directory among several arguments: ingest it as its own
|
|
143
|
+
// bundle would be ambiguous, so refuse plainly.
|
|
144
|
+
process.stderr.write(`error: ${target} is a directory; pass one bundle path, or a list of files\n`);
|
|
145
|
+
return 3;
|
|
146
|
+
}
|
|
147
|
+
entries.push({ path: basename(target), open: () => readFile(target).then((b) => new Uint8Array(b)) });
|
|
148
|
+
}
|
|
149
|
+
ingest = await ingestEntries(entries);
|
|
150
|
+
}
|
|
151
|
+
} catch (err) {
|
|
152
|
+
process.stderr.write(`error: cannot read bundle: ${(err as Error).message}\n`);
|
|
153
|
+
return 3;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const report = await checkIngest(ingest);
|
|
157
|
+
const bytes = args.json ? serializeCheckReport(report) : renderCheckText(report);
|
|
158
|
+
if (args.outFile !== undefined) {
|
|
159
|
+
writeFileSync(args.outFile, bytes);
|
|
160
|
+
} else {
|
|
161
|
+
process.stdout.write(bytes);
|
|
162
|
+
}
|
|
163
|
+
return report.result === "TRUE" ? 0 : report.result === "FALSE" ? 1 : 2;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function looksLikeArchive(path: string): boolean {
|
|
167
|
+
const lower = path.toLowerCase();
|
|
168
|
+
return lower.endsWith(".tar") || lower.endsWith(".tar.gz") || lower.endsWith(".tgz");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function runInit(args: ParsedArgs): Promise<number> {
|
|
172
|
+
if (args.summary || args.json) return usage();
|
|
109
173
|
const files = args.positional;
|
|
110
174
|
if (files.length === 0) return usage();
|
|
111
175
|
if (args.outFile !== undefined && existsSync(args.outFile)) {
|
|
@@ -161,7 +225,7 @@ async function runInit(args: ParsedArgs): Promise<number> {
|
|
|
161
225
|
}
|
|
162
226
|
|
|
163
227
|
async function runEvaluate(args: ParsedArgs): Promise<number> {
|
|
164
|
-
if (args.positional.length !== 2) return usage();
|
|
228
|
+
if (args.json || args.positional.length !== 2) return usage();
|
|
165
229
|
const [rulePath, bundlePath] = args.positional as [string, string];
|
|
166
230
|
|
|
167
231
|
let result;
|
|
@@ -192,22 +256,24 @@ async function main(): Promise<number> {
|
|
|
192
256
|
const argv = process.argv.slice(2);
|
|
193
257
|
// "--" as the first token forces evaluate mode: parseArgs treats
|
|
194
258
|
// everything after it as positional, so a rule file literally named
|
|
195
|
-
// "init" is reachable as `bitgraph-play -- init <bundle>`.
|
|
196
|
-
const
|
|
197
|
-
if (
|
|
259
|
+
// "init" or "check" is reachable as `bitgraph-play -- init <bundle>`.
|
|
260
|
+
const subcommand = argv[0] === "init" || argv[0] === "check" ? argv[0] : undefined;
|
|
261
|
+
if (subcommand !== undefined && existsSync(subcommand)) {
|
|
198
262
|
// Both readings are plausible here; a silent pick would hand a
|
|
199
263
|
// 0.1.1 caller a skeleton with exit 0 where the published contract
|
|
200
264
|
// returned a verdict. Refuse loudly instead.
|
|
201
265
|
process.stderr.write(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
266
|
+
`error: a file named "${subcommand}" exists here, so this command is ambiguous.\n` +
|
|
267
|
+
` to evaluate it as a rule: bitgraph-play -- ${subcommand} <bundle> (or ./${subcommand})\n` +
|
|
268
|
+
` to run the ${subcommand} subcommand: rename that file or run from another directory\n`
|
|
205
269
|
);
|
|
206
270
|
return 3;
|
|
207
271
|
}
|
|
208
|
-
const parsed = parseArgs(
|
|
272
|
+
const parsed = parseArgs(subcommand !== undefined ? argv.slice(1) : argv);
|
|
209
273
|
if (parsed === undefined) return usage();
|
|
210
|
-
|
|
274
|
+
if (subcommand === "init") return runInit(parsed);
|
|
275
|
+
if (subcommand === "check") return runCheck(parsed);
|
|
276
|
+
return runEvaluate(parsed);
|
|
211
277
|
}
|
|
212
278
|
|
|
213
279
|
main().then(
|
package/src/index.ts
CHANGED
|
@@ -44,3 +44,6 @@ export { kleeneAll, kleeneAny, kleeneNot } from "./logic.js";
|
|
|
44
44
|
export { evaluate } from "./evaluate.js";
|
|
45
45
|
export type { Evaluation } from "./evaluate.js";
|
|
46
46
|
export { buildVerdict, serializeVerdict, playerVersion, PLAYER_VERSION } from "./verdict.js";
|
|
47
|
+
|
|
48
|
+
export { checkIngest, buildCheckReport, renderCheckText, serializeCheckReport, KNOWN_ENCLAVE_MEASUREMENTS } from "./check.js";
|
|
49
|
+
export type { CheckReport, CheckRecording, CheckAnchor, CheckLine, CheckBounds, CheckBound, CheckOptions } from "./check.js";
|
package/src/verdict.ts
CHANGED
|
@@ -31,7 +31,7 @@ import type { DeclaredEntry, Resolution, Rule, Verdict } from "./types.js";
|
|
|
31
31
|
* A unit test asserts this equals package.json's version, so the
|
|
32
32
|
* constant cannot drift silently across releases.
|
|
33
33
|
*/
|
|
34
|
-
export const PLAYER_VERSION = "0.
|
|
34
|
+
export const PLAYER_VERSION = "0.5.0";
|
|
35
35
|
|
|
36
36
|
/** The player package's own version. */
|
|
37
37
|
export function playerVersion(): string {
|