@hawkeyexl/manni 2.0.0 → 2.1.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 +4 -4
- package/dist/{chunk-XU2QTJQQ.js → chunk-2DC2GSRH.js} +2079 -312
- package/dist/chunk-2DC2GSRH.js.map +1 -0
- package/dist/{chunk-GGVNTB7W.js → chunk-HNPRXGD7.js} +16 -58
- package/dist/chunk-HNPRXGD7.js.map +1 -0
- package/dist/{chunk-7ZN43F3Y.js → chunk-UTXTVCBV.js} +156 -547
- package/dist/chunk-UTXTVCBV.js.map +1 -0
- package/dist/cli.js +10 -18
- package/dist/cli.js.map +1 -1
- package/dist/docmeta.js +4 -3
- package/dist/docmeta.js.map +1 -1
- package/dist/index.d.ts +257 -68
- package/dist/index.js +2 -2
- package/package.json +3 -1
- package/dist/chunk-7ZN43F3Y.js.map +0 -1
- package/dist/chunk-GGVNTB7W.js.map +0 -1
- package/dist/chunk-XU2QTJQQ.js.map +0 -1
|
@@ -8,7 +8,7 @@ var __export = (target, all) => {
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@hawkeyexl/manni",
|
|
11
|
-
version: "2.
|
|
11
|
+
version: "2.1.0",
|
|
12
12
|
description: "One command line for documentation that is meant to be checked. `manni meta` validates the presence and format of document metadata (Markdown frontmatter and more) against JSON Schema, built for CI. Formerly docmeta.",
|
|
13
13
|
type: "module",
|
|
14
14
|
bin: {
|
|
@@ -60,6 +60,8 @@ var package_default = {
|
|
|
60
60
|
"docs:check-api": "node scripts/check-api-reference.mjs",
|
|
61
61
|
"docs:check-links": "node scripts/check-doc-links.mjs",
|
|
62
62
|
"docs:check-tables": "node scripts/check-table-rows.mjs",
|
|
63
|
+
"docs:check-versions": "node scripts/check-versions.mjs",
|
|
64
|
+
"docs:sync-versions": "node scripts/sync-versions.mjs",
|
|
63
65
|
"schemas:sync": "node scripts/sync-builtin-schemas.mjs",
|
|
64
66
|
"schemas:check": "node scripts/check-builtin-schemas.mjs",
|
|
65
67
|
"schemas:check-published": "node scripts/check-published-schemas.mjs",
|
|
@@ -122,6 +124,85 @@ var package_default = {
|
|
|
122
124
|
}
|
|
123
125
|
};
|
|
124
126
|
|
|
127
|
+
// src/shared/run.ts
|
|
128
|
+
import { realpathSync } from "node:fs";
|
|
129
|
+
import { fileURLToPath } from "node:url";
|
|
130
|
+
import { CommanderError } from "commander";
|
|
131
|
+
|
|
132
|
+
// src/shared/errors.ts
|
|
133
|
+
var ToolError = class extends Error {
|
|
134
|
+
/** clig.dev: 2 is operational/usage; 1 is reserved for findings. */
|
|
135
|
+
exitCode = 2;
|
|
136
|
+
constructor(message) {
|
|
137
|
+
super(message);
|
|
138
|
+
this.name = "ToolError";
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
function errorMessage(err) {
|
|
142
|
+
if (err instanceof Error) return err.message;
|
|
143
|
+
if (typeof err === "string") return err;
|
|
144
|
+
if (typeof err === "number" || typeof err === "bigint") return String(err);
|
|
145
|
+
if (typeof err === "object" && err !== null && "message" in err && typeof err.message === "string") {
|
|
146
|
+
return err.message;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const json = JSON.stringify(err);
|
|
150
|
+
if (json !== void 0) return json;
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
return String(err);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/shared/program-name.ts
|
|
157
|
+
var name = "manni";
|
|
158
|
+
function programName() {
|
|
159
|
+
return name;
|
|
160
|
+
}
|
|
161
|
+
function setProgramName(value) {
|
|
162
|
+
name = value;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/shared/run.ts
|
|
166
|
+
function fail(err) {
|
|
167
|
+
const msg = err instanceof ToolError ? err.message : `Unexpected error: ${errorMessage(err)}`;
|
|
168
|
+
process.stderr.write(`${programName()}: ${msg}
|
|
169
|
+
`);
|
|
170
|
+
process.exit(2);
|
|
171
|
+
}
|
|
172
|
+
var STDIN_LINES_MARKER = "\0";
|
|
173
|
+
var STDIN_WITH_LINES = /^-(:[1-9][0-9]*(?:-[1-9][0-9]*)?)$/;
|
|
174
|
+
function normalizeStdinArgv(argv) {
|
|
175
|
+
return argv.map((arg) => {
|
|
176
|
+
const lines = STDIN_WITH_LINES.exec(arg)?.[1];
|
|
177
|
+
return lines === void 0 ? arg : `${STDIN_LINES_MARKER}${lines}`;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async function runProgram(program, argv = process.argv) {
|
|
181
|
+
setProgramName(program.name());
|
|
182
|
+
try {
|
|
183
|
+
await program.parseAsync(normalizeStdinArgv(argv));
|
|
184
|
+
} catch (err) {
|
|
185
|
+
if (err instanceof CommanderError) {
|
|
186
|
+
process.exitCode = err.exitCode === 0 ? 0 : 2;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
throw err;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function isMainModule(moduleUrl) {
|
|
193
|
+
const entry = process.argv[1];
|
|
194
|
+
if (!entry) return false;
|
|
195
|
+
try {
|
|
196
|
+
return realpathSync(entry) === fileURLToPath(moduleUrl);
|
|
197
|
+
} catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function runIfMain(moduleUrl, build) {
|
|
202
|
+
if (!isMainModule(moduleUrl)) return;
|
|
203
|
+
runProgram(build()).catch(fail);
|
|
204
|
+
}
|
|
205
|
+
|
|
125
206
|
// src/shared/collections.ts
|
|
126
207
|
import picomatch2 from "picomatch";
|
|
127
208
|
|
|
@@ -390,13 +471,53 @@ function isMember(collection, relPath) {
|
|
|
390
471
|
return !collection.exclude.some((glob) => matchesFileGlob(relPath, glob));
|
|
391
472
|
}
|
|
392
473
|
function normalizeEntry(entry) {
|
|
393
|
-
const
|
|
394
|
-
return
|
|
474
|
+
const posix4 = entry.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
475
|
+
return posix4.endsWith("/") ? posix4.slice(0, -1) : posix4;
|
|
395
476
|
}
|
|
396
477
|
function isGlob(entry) {
|
|
397
478
|
return picomatch2.scan(entry).isGlob;
|
|
398
479
|
}
|
|
399
480
|
|
|
481
|
+
// src/meta/core/integrity.ts
|
|
482
|
+
import { createHash } from "node:crypto";
|
|
483
|
+
var INTEGRITY_ALGORITHM = "sha256";
|
|
484
|
+
var INTEGRITY_SHAPE = `${INTEGRITY_ALGORITHM}-<64 hex characters>`;
|
|
485
|
+
var INTEGRITY_RE = new RegExp(`^${INTEGRITY_ALGORITHM}-([0-9a-f]{64})$`);
|
|
486
|
+
function integrityOf(bytes) {
|
|
487
|
+
return `${INTEGRITY_ALGORITHM}-${createHash(INTEGRITY_ALGORITHM).update(bytes).digest("hex")}`;
|
|
488
|
+
}
|
|
489
|
+
function isIntegrity(value) {
|
|
490
|
+
return INTEGRITY_RE.test(value);
|
|
491
|
+
}
|
|
492
|
+
function toLf(bytes) {
|
|
493
|
+
const out = new Uint8Array(bytes.length);
|
|
494
|
+
let n = 0;
|
|
495
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
496
|
+
const byte = bytes[i];
|
|
497
|
+
if (byte === void 0) continue;
|
|
498
|
+
if (byte === 13 && bytes[i + 1] === 10) continue;
|
|
499
|
+
out[n++] = byte;
|
|
500
|
+
}
|
|
501
|
+
return out.subarray(0, n);
|
|
502
|
+
}
|
|
503
|
+
function toCrlf(bytes) {
|
|
504
|
+
const lf = toLf(bytes);
|
|
505
|
+
const out = new Uint8Array(lf.length * 2);
|
|
506
|
+
let n = 0;
|
|
507
|
+
for (let i = 0; i < lf.length; i++) {
|
|
508
|
+
const byte = lf[i];
|
|
509
|
+
if (byte === void 0) continue;
|
|
510
|
+
if (byte === 10) out[n++] = 13;
|
|
511
|
+
out[n++] = byte;
|
|
512
|
+
}
|
|
513
|
+
return out.subarray(0, n);
|
|
514
|
+
}
|
|
515
|
+
function diagnoseIntegrity(bytes, expected) {
|
|
516
|
+
if (integrityOf(toLf(bytes)) === expected) return "line-endings";
|
|
517
|
+
if (integrityOf(toCrlf(bytes)) === expected) return "line-endings";
|
|
518
|
+
return "content";
|
|
519
|
+
}
|
|
520
|
+
|
|
400
521
|
// src/meta/extractors/frontmatter.ts
|
|
401
522
|
import { parseDocument, LineCounter, isMap, isSeq, isScalar } from "yaml";
|
|
402
523
|
import { parse as parseToml } from "smol-toml";
|
|
@@ -451,22 +572,22 @@ function locateFrontmatter(content) {
|
|
|
451
572
|
while (cursor <= body.length) {
|
|
452
573
|
const nl = body.indexOf("\n", cursor);
|
|
453
574
|
const hasNl = nl !== -1;
|
|
454
|
-
const
|
|
455
|
-
const hadCr =
|
|
456
|
-
const line = body.slice(cursor, hadCr ?
|
|
575
|
+
const lineEnd2 = hasNl ? nl : body.length;
|
|
576
|
+
const hadCr = lineEnd2 > cursor && body.charCodeAt(lineEnd2 - 1) === 13;
|
|
577
|
+
const line = body.slice(cursor, hadCr ? lineEnd2 - 1 : lineEnd2);
|
|
457
578
|
if (fence.isClose(line)) {
|
|
458
579
|
return {
|
|
459
580
|
flavor: fence.flavor,
|
|
460
581
|
openStart: bom,
|
|
461
582
|
innerStart: innerStart + bom,
|
|
462
583
|
innerEnd: cursor + bom,
|
|
463
|
-
closeEnd: (hasNl ?
|
|
584
|
+
closeEnd: (hasNl ? lineEnd2 + 1 : lineEnd2) + bom,
|
|
464
585
|
eol,
|
|
465
586
|
firstContentLine: 2
|
|
466
587
|
};
|
|
467
588
|
}
|
|
468
589
|
if (!hasNl) break;
|
|
469
|
-
cursor =
|
|
590
|
+
cursor = lineEnd2 + 1;
|
|
470
591
|
}
|
|
471
592
|
return null;
|
|
472
593
|
}
|
|
@@ -605,30 +726,6 @@ function extractFrontmatter(content, format) {
|
|
|
605
726
|
}
|
|
606
727
|
}
|
|
607
728
|
|
|
608
|
-
// src/shared/errors.ts
|
|
609
|
-
var ToolError = class extends Error {
|
|
610
|
-
/** clig.dev: 2 is operational/usage; 1 is reserved for findings. */
|
|
611
|
-
exitCode = 2;
|
|
612
|
-
constructor(message) {
|
|
613
|
-
super(message);
|
|
614
|
-
this.name = "ToolError";
|
|
615
|
-
}
|
|
616
|
-
};
|
|
617
|
-
function errorMessage(err) {
|
|
618
|
-
if (err instanceof Error) return err.message;
|
|
619
|
-
if (typeof err === "string") return err;
|
|
620
|
-
if (typeof err === "number" || typeof err === "bigint") return String(err);
|
|
621
|
-
if (typeof err === "object" && err !== null && "message" in err && typeof err.message === "string") {
|
|
622
|
-
return err.message;
|
|
623
|
-
}
|
|
624
|
-
try {
|
|
625
|
-
const json = JSON.stringify(err);
|
|
626
|
-
if (json !== void 0) return json;
|
|
627
|
-
} catch {
|
|
628
|
-
}
|
|
629
|
-
return String(err);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
729
|
// src/meta/types.ts
|
|
633
730
|
function isErrorSeverity(e) {
|
|
634
731
|
return e.severity === void 0 || e.severity === "error";
|
|
@@ -1202,8 +1299,8 @@ function ditaShape(content, root, filePath) {
|
|
|
1202
1299
|
const lower = filePath?.toLowerCase() ?? "";
|
|
1203
1300
|
const byExtension2 = (lower.endsWith(".dita") || lower.endsWith(".ditamap")) && DITA_ROOTS.has(name2);
|
|
1204
1301
|
if (!byDoctype && !byClass && !byArch && !byExtension2) return void 0;
|
|
1205
|
-
const
|
|
1206
|
-
return
|
|
1302
|
+
const isMap7 = /^\s*[-+]\s+map\//.test(cls) || MAP_ROOTS.has(name2) || lower.endsWith(".ditamap");
|
|
1303
|
+
return isMap7 ? { kind: "map", container: "topicmeta", preamble: /* @__PURE__ */ new Set(["title"]) } : {
|
|
1207
1304
|
kind: "topic",
|
|
1208
1305
|
container: "metadata",
|
|
1209
1306
|
preamble: /* @__PURE__ */ new Set([
|
|
@@ -2631,24 +2728,13 @@ function listFormats() {
|
|
|
2631
2728
|
// src/meta/core/write-file.ts
|
|
2632
2729
|
import { writeFile, rename, rm, stat, chmod } from "node:fs/promises";
|
|
2633
2730
|
import { dirname, join, basename } from "node:path";
|
|
2634
|
-
|
|
2635
|
-
// src/shared/program-name.ts
|
|
2636
|
-
var name = "manni";
|
|
2637
|
-
function programName() {
|
|
2638
|
-
return name;
|
|
2639
|
-
}
|
|
2640
|
-
function setProgramName(value) {
|
|
2641
|
-
name = value;
|
|
2642
|
-
}
|
|
2643
|
-
|
|
2644
|
-
// src/meta/core/write-file.ts
|
|
2645
2731
|
var LOCKED = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
|
|
2646
2732
|
var RENAME_ATTEMPTS = 3;
|
|
2647
2733
|
function errorCode(err) {
|
|
2648
2734
|
return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
2649
2735
|
}
|
|
2650
|
-
var wait = (ms) => new Promise((
|
|
2651
|
-
setTimeout(
|
|
2736
|
+
var wait = (ms) => new Promise((resolve19) => {
|
|
2737
|
+
setTimeout(resolve19, ms);
|
|
2652
2738
|
});
|
|
2653
2739
|
async function writeFileAtomic(path, contents) {
|
|
2654
2740
|
const tmp = join(
|
|
@@ -2689,7 +2775,7 @@ async function writeFileAtomic(path, contents) {
|
|
|
2689
2775
|
}
|
|
2690
2776
|
|
|
2691
2777
|
// src/meta/core/schema-cache.ts
|
|
2692
|
-
import { createHash } from "node:crypto";
|
|
2778
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2693
2779
|
import { mkdir, readFile, stat as stat2 } from "node:fs/promises";
|
|
2694
2780
|
import { join as join2 } from "node:path";
|
|
2695
2781
|
var SCHEMA_CACHE_DIR = ".manni/meta/schema-cache";
|
|
@@ -2719,7 +2805,7 @@ var SchemaCache = class {
|
|
|
2719
2805
|
* directory.
|
|
2720
2806
|
*/
|
|
2721
2807
|
entryPath(url) {
|
|
2722
|
-
const key =
|
|
2808
|
+
const key = createHash2("sha256").update(url).digest("hex");
|
|
2723
2809
|
return join2(this.dir, `${key}.json`);
|
|
2724
2810
|
}
|
|
2725
2811
|
/**
|
|
@@ -2796,46 +2882,6 @@ var SchemaCache = class {
|
|
|
2796
2882
|
}
|
|
2797
2883
|
};
|
|
2798
2884
|
|
|
2799
|
-
// src/meta/core/integrity.ts
|
|
2800
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
2801
|
-
var INTEGRITY_ALGORITHM = "sha256";
|
|
2802
|
-
var INTEGRITY_SHAPE = `${INTEGRITY_ALGORITHM}-<64 hex characters>`;
|
|
2803
|
-
var INTEGRITY_RE = new RegExp(`^${INTEGRITY_ALGORITHM}-([0-9a-f]{64})$`);
|
|
2804
|
-
function integrityOf(bytes) {
|
|
2805
|
-
return `${INTEGRITY_ALGORITHM}-${createHash2(INTEGRITY_ALGORITHM).update(bytes).digest("hex")}`;
|
|
2806
|
-
}
|
|
2807
|
-
function isIntegrity(value) {
|
|
2808
|
-
return INTEGRITY_RE.test(value);
|
|
2809
|
-
}
|
|
2810
|
-
function toLf(bytes) {
|
|
2811
|
-
const out = new Uint8Array(bytes.length);
|
|
2812
|
-
let n = 0;
|
|
2813
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
2814
|
-
const byte = bytes[i];
|
|
2815
|
-
if (byte === void 0) continue;
|
|
2816
|
-
if (byte === 13 && bytes[i + 1] === 10) continue;
|
|
2817
|
-
out[n++] = byte;
|
|
2818
|
-
}
|
|
2819
|
-
return out.subarray(0, n);
|
|
2820
|
-
}
|
|
2821
|
-
function toCrlf(bytes) {
|
|
2822
|
-
const lf = toLf(bytes);
|
|
2823
|
-
const out = new Uint8Array(lf.length * 2);
|
|
2824
|
-
let n = 0;
|
|
2825
|
-
for (let i = 0; i < lf.length; i++) {
|
|
2826
|
-
const byte = lf[i];
|
|
2827
|
-
if (byte === void 0) continue;
|
|
2828
|
-
if (byte === 10) out[n++] = 13;
|
|
2829
|
-
out[n++] = byte;
|
|
2830
|
-
}
|
|
2831
|
-
return out.subarray(0, n);
|
|
2832
|
-
}
|
|
2833
|
-
function diagnoseIntegrity(bytes, expected) {
|
|
2834
|
-
if (integrityOf(toLf(bytes)) === expected) return "line-endings";
|
|
2835
|
-
if (integrityOf(toCrlf(bytes)) === expected) return "line-endings";
|
|
2836
|
-
return "content";
|
|
2837
|
-
}
|
|
2838
|
-
|
|
2839
2885
|
// src/meta/core/schema-registry.ts
|
|
2840
2886
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
2841
2887
|
import { resolve as resolvePath } from "node:path";
|
|
@@ -5241,8 +5287,8 @@ var SCHEMA_KEYS = [
|
|
|
5241
5287
|
"multipleOf"
|
|
5242
5288
|
];
|
|
5243
5289
|
var RETRY_DELAY_MS = 500;
|
|
5244
|
-
var wait2 = (ms) => new Promise((
|
|
5245
|
-
setTimeout(
|
|
5290
|
+
var wait2 = (ms) => new Promise((resolve19) => {
|
|
5291
|
+
setTimeout(resolve19, ms);
|
|
5246
5292
|
});
|
|
5247
5293
|
var RetryableFetchError = class extends DocmetaError {
|
|
5248
5294
|
};
|
|
@@ -5657,8 +5703,10 @@ var DERIVABLE_FIELDS = [
|
|
|
5657
5703
|
"authors",
|
|
5658
5704
|
"owner",
|
|
5659
5705
|
"reviewed-by",
|
|
5660
|
-
"last-reviewed"
|
|
5706
|
+
"last-reviewed",
|
|
5707
|
+
"provenance"
|
|
5661
5708
|
];
|
|
5709
|
+
var PROVENANCE_FIELD = "provenance";
|
|
5662
5710
|
function isBuiltinField(x) {
|
|
5663
5711
|
return DERIVABLE_FIELDS.includes(x);
|
|
5664
5712
|
}
|
|
@@ -6143,7 +6191,7 @@ function asElementPaths(value, where, source) {
|
|
|
6143
6191
|
}
|
|
6144
6192
|
var OVERRIDE_KEYS = ["collection", "files", "schemas", "elements"];
|
|
6145
6193
|
var CHECK_KEYS = ["name", "query"];
|
|
6146
|
-
var DERIVE_KEYS = ["fields", "sources", "codeowners", "commands"];
|
|
6194
|
+
var DERIVE_KEYS = ["fields", "sources", "codeowners", "commands", "machines"];
|
|
6147
6195
|
var DERIVE_COMMAND_KEYS = ["run", "timeout"];
|
|
6148
6196
|
var BUILTIN_CLAIMED_BY = {
|
|
6149
6197
|
created: "git",
|
|
@@ -6151,7 +6199,8 @@ var BUILTIN_CLAIMED_BY = {
|
|
|
6151
6199
|
authors: "git",
|
|
6152
6200
|
owner: "codeowners",
|
|
6153
6201
|
"reviewed-by": "GitHub or GitLab",
|
|
6154
|
-
"last-reviewed": "GitHub or GitLab"
|
|
6202
|
+
"last-reviewed": "GitHub or GitLab",
|
|
6203
|
+
provenance: "git"
|
|
6155
6204
|
};
|
|
6156
6205
|
var DERIVED_TABLE_RESERVED = /* @__PURE__ */ new Set(["_path", "_sources"]);
|
|
6157
6206
|
var DERIVABLE_LIST = `Derivable fields: ${DERIVABLE_FIELDS.join(", ")}, or any key with an entry in derive.commands.`;
|
|
@@ -6396,6 +6445,7 @@ function assertManagedFieldsUnowned(config, collections, source, section) {
|
|
|
6396
6445
|
if (owner !== void 0) refuse(`derive.commands.${key}`, owner);
|
|
6397
6446
|
}
|
|
6398
6447
|
for (const [i, field] of (config.derive?.fields ?? []).entries()) {
|
|
6448
|
+
if (field === PROVENANCE_FIELD) continue;
|
|
6399
6449
|
const owner = manifestOwning(field, collections);
|
|
6400
6450
|
if (owner !== void 0) refuse(`derive.fields[${i}] "${field}"`, owner);
|
|
6401
6451
|
}
|
|
@@ -6408,9 +6458,9 @@ function parseDerive(raw, source) {
|
|
|
6408
6458
|
}
|
|
6409
6459
|
const e = raw;
|
|
6410
6460
|
rejectUnknownKeys2(e, DERIVE_KEYS, '"derive"', source);
|
|
6411
|
-
if (e.fields === void 0 && e.sources === void 0 && e.codeowners === void 0 && e.commands === void 0) {
|
|
6461
|
+
if (e.fields === void 0 && e.sources === void 0 && e.codeowners === void 0 && e.commands === void 0 && e.machines === void 0) {
|
|
6412
6462
|
throw new DocmetaError(
|
|
6413
|
-
`${source}: "derive" sets nothing. Give it \`fields:\` to manage, \`commands:\` to derive from a command, or \`sources:\` or \`
|
|
6463
|
+
`${source}: "derive" sets nothing. Give it \`fields:\` to manage, \`commands:\` to derive from a command, or \`sources:\`, \`codeowners:\` or \`machines:\` to shape the reads.`
|
|
6414
6464
|
);
|
|
6415
6465
|
}
|
|
6416
6466
|
const commands = e.commands === void 0 ? void 0 : parseDeriveCommands(e.commands, source);
|
|
@@ -6461,6 +6511,24 @@ function parseDerive(raw, source) {
|
|
|
6461
6511
|
derive.codeowners = e.codeowners;
|
|
6462
6512
|
}
|
|
6463
6513
|
if (commands !== void 0) derive.commands = commands;
|
|
6514
|
+
if (e.machines !== void 0) {
|
|
6515
|
+
if (!Array.isArray(e.machines) || e.machines.length === 0 || e.machines.some((m) => typeof m !== "string")) {
|
|
6516
|
+
throw new DocmetaError(
|
|
6517
|
+
`${source}: derive.machines must be a non-empty list of globs, matched against a trailer's name and its email. Default: ["*[bot]"].`
|
|
6518
|
+
);
|
|
6519
|
+
}
|
|
6520
|
+
const machines = [];
|
|
6521
|
+
e.machines.forEach((m, i) => {
|
|
6522
|
+
if (m.trim() === "") {
|
|
6523
|
+
throw new DocmetaError(`${source}: derive.machines[${i}] is blank; a glob must name something.`);
|
|
6524
|
+
}
|
|
6525
|
+
if (machines.includes(m)) {
|
|
6526
|
+
throw new DocmetaError(`${source}: derive.machines lists "${m}" twice.`);
|
|
6527
|
+
}
|
|
6528
|
+
machines.push(m);
|
|
6529
|
+
});
|
|
6530
|
+
derive.machines = machines;
|
|
6531
|
+
}
|
|
6464
6532
|
return derive;
|
|
6465
6533
|
}
|
|
6466
6534
|
function parseDeriveCommands(raw, source) {
|
|
@@ -7741,8 +7809,8 @@ var RENAME_ATTEMPTS2 = 3;
|
|
|
7741
7809
|
function errorCode2(err) {
|
|
7742
7810
|
return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
7743
7811
|
}
|
|
7744
|
-
var wait3 = (ms) => new Promise((
|
|
7745
|
-
setTimeout(
|
|
7812
|
+
var wait3 = (ms) => new Promise((resolve19) => {
|
|
7813
|
+
setTimeout(resolve19, ms);
|
|
7746
7814
|
});
|
|
7747
7815
|
async function writeTextAtomic(path, text2) {
|
|
7748
7816
|
const tmp = join5(
|
|
@@ -8968,8 +9036,8 @@ var ReviewCache = class {
|
|
|
8968
9036
|
}
|
|
8969
9037
|
if (!isRecord2(raw)) return void 0;
|
|
8970
9038
|
if (raw.version !== REVIEW_CACHE_VERSION || raw.key !== key) return void 0;
|
|
8971
|
-
const
|
|
8972
|
-
return
|
|
9039
|
+
const change = readChange(raw.change);
|
|
9040
|
+
return change ?? void 0;
|
|
8973
9041
|
}
|
|
8974
9042
|
set(key, value) {
|
|
8975
9043
|
if (!this.enabled) return;
|
|
@@ -8998,9 +9066,9 @@ function cachedClient(client, cache, identity) {
|
|
|
8998
9066
|
const key = `${identity.host}/${identity.project}/${sha}`;
|
|
8999
9067
|
const hit = cache.get(key);
|
|
9000
9068
|
if (hit !== void 0) return hit;
|
|
9001
|
-
const
|
|
9002
|
-
if (
|
|
9003
|
-
return
|
|
9069
|
+
const change = await client.mergedChangeFor(sha);
|
|
9070
|
+
if (change !== null) cache.set(key, change);
|
|
9071
|
+
return change;
|
|
9004
9072
|
}
|
|
9005
9073
|
};
|
|
9006
9074
|
}
|
|
@@ -9035,7 +9103,7 @@ function run(bin, args, opts) {
|
|
|
9035
9103
|
done = true;
|
|
9036
9104
|
settle(r);
|
|
9037
9105
|
};
|
|
9038
|
-
const
|
|
9106
|
+
const fail2 = (err) => {
|
|
9039
9107
|
if (done) return;
|
|
9040
9108
|
done = true;
|
|
9041
9109
|
reject(err);
|
|
@@ -9047,7 +9115,7 @@ function run(bin, args, opts) {
|
|
|
9047
9115
|
windowsHide: true
|
|
9048
9116
|
});
|
|
9049
9117
|
} catch (err) {
|
|
9050
|
-
|
|
9118
|
+
fail2(err instanceof Error ? err : new Error(String(err)));
|
|
9051
9119
|
return;
|
|
9052
9120
|
}
|
|
9053
9121
|
let stdout = "";
|
|
@@ -9083,7 +9151,7 @@ function run(bin, args, opts) {
|
|
|
9083
9151
|
}, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2);
|
|
9084
9152
|
child.on("error", (err) => {
|
|
9085
9153
|
clearTimeout(timer);
|
|
9086
|
-
|
|
9154
|
+
fail2(err.code === "ENOENT" ? new BinMissing(err.message) : err);
|
|
9087
9155
|
});
|
|
9088
9156
|
child.on("close", (code) => {
|
|
9089
9157
|
clearTimeout(timer);
|
|
@@ -9494,17 +9562,17 @@ async function deriveFromReviews(inputs, client) {
|
|
|
9494
9562
|
records.set(input.label, NOTHING);
|
|
9495
9563
|
continue;
|
|
9496
9564
|
}
|
|
9497
|
-
const
|
|
9498
|
-
records.set(input.label, factsFrom(
|
|
9565
|
+
const change = await changeFor(input.sha);
|
|
9566
|
+
records.set(input.label, factsFrom(change, kind));
|
|
9499
9567
|
}
|
|
9500
9568
|
return { status, records };
|
|
9501
9569
|
}
|
|
9502
|
-
function factsFrom(
|
|
9503
|
-
if (
|
|
9504
|
-
const ordered = byTime(
|
|
9570
|
+
function factsFrom(change, kind) {
|
|
9571
|
+
if (change === null || change.approvals.length === 0) return NOTHING;
|
|
9572
|
+
const ordered = byTime(change.approvals);
|
|
9505
9573
|
const latest = ordered.at(-1);
|
|
9506
9574
|
if (latest === void 0) return NOTHING;
|
|
9507
|
-
const evidence = kind === "gitlab" ? `gitlab MR !${
|
|
9575
|
+
const evidence = kind === "gitlab" ? `gitlab MR !${change.id}` : `github PR #${change.id}`;
|
|
9508
9576
|
const dateEvidence = kind === "gitlab" ? `${evidence} (merge date; GitLab records no approval time)` : evidence;
|
|
9509
9577
|
return {
|
|
9510
9578
|
"reviewed-by": {
|
|
@@ -9742,87 +9810,631 @@ function toPosix2(p) {
|
|
|
9742
9810
|
// src/meta/core/derive/git.ts
|
|
9743
9811
|
import { constants as bufferConstants } from "node:buffer";
|
|
9744
9812
|
import { spawn as spawn3 } from "node:child_process";
|
|
9745
|
-
import { dirname as dirname8, extname as extname2, relative as relative10, sep as sep5 } from "node:path";
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
);
|
|
9752
|
-
|
|
9753
|
-
};
|
|
9754
|
-
var BlobsUnreadable = class extends Error {
|
|
9755
|
-
constructor(detail) {
|
|
9756
|
-
super(detail);
|
|
9757
|
-
this.detail = detail;
|
|
9813
|
+
import { dirname as dirname8, extname as extname2, isAbsolute as isAbsolute5, posix as posix2, relative as relative10, sep as sep5 } from "node:path";
|
|
9814
|
+
import { parse as parseYaml4 } from "yaml";
|
|
9815
|
+
|
|
9816
|
+
// src/shared/pin.ts
|
|
9817
|
+
var LineRangeError = class extends ToolError {
|
|
9818
|
+
constructor(message) {
|
|
9819
|
+
super(message);
|
|
9820
|
+
this.name = "LineRangeError";
|
|
9758
9821
|
}
|
|
9759
|
-
detail;
|
|
9760
9822
|
};
|
|
9761
|
-
function
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9823
|
+
function normalizeText(text2) {
|
|
9824
|
+
const body = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
|
|
9825
|
+
return body.replace(/\r\n/g, "\n");
|
|
9826
|
+
}
|
|
9827
|
+
function splitLines(text2) {
|
|
9828
|
+
const parts = normalizeText(text2).split("\n");
|
|
9829
|
+
if (parts.length > 0 && parts[parts.length - 1] === "") parts.pop();
|
|
9830
|
+
return parts;
|
|
9831
|
+
}
|
|
9832
|
+
function sliceLines(lines, range, label) {
|
|
9833
|
+
const start = range?.start;
|
|
9834
|
+
if (start === void 0) return lines.join("\n");
|
|
9835
|
+
const end = range?.end ?? start;
|
|
9836
|
+
if (end < start) {
|
|
9837
|
+
throw new LineRangeError(`${label ?? "the source"} range ${String(start)}-${String(end)} ends before it starts.`);
|
|
9838
|
+
}
|
|
9839
|
+
if (start > lines.length || end > lines.length) {
|
|
9840
|
+
const first = start > lines.length ? start : end;
|
|
9841
|
+
throw new LineRangeError(`${label ?? "the source"} has ${lines.length} lines; line ${first} is out of range.`);
|
|
9842
|
+
}
|
|
9843
|
+
return lines.slice(start - 1, end).join("\n");
|
|
9844
|
+
}
|
|
9845
|
+
function hashRange(text2, range, key) {
|
|
9846
|
+
return hashLines(sliceLines(splitLines(text2), range), key);
|
|
9847
|
+
}
|
|
9848
|
+
function hashLines(joined, key) {
|
|
9849
|
+
return key === void 0 ? integrityOf(Buffer.from(joined, "utf8")) : keyedPin(joined, key);
|
|
9850
|
+
}
|
|
9851
|
+
function isKeyedPin(pin) {
|
|
9852
|
+
return pin.startsWith(KEYED_PIN_PREFIX);
|
|
9853
|
+
}
|
|
9854
|
+
function pinOfLines(lines, range) {
|
|
9855
|
+
if (range.start < 1 || range.end > lines.length || range.end < range.start) return void 0;
|
|
9856
|
+
return hashLines(lines.slice(range.start - 1, range.end).join("\n"));
|
|
9857
|
+
}
|
|
9858
|
+
var MOVE_WINDOW_LINES = 2e3;
|
|
9859
|
+
var MOVE_BUDGET_BYTES = 64 * 1024 * 1024;
|
|
9860
|
+
var MAX_RANGE_LINES = 5e3;
|
|
9861
|
+
function findWindows(lines, length, pin, key, opts) {
|
|
9862
|
+
const starts = [];
|
|
9863
|
+
const lastStart = lines.length - length + 1;
|
|
9864
|
+
if (length < 1 || lastStart < 1) return { starts, truncated: false };
|
|
9865
|
+
const joined = (start) => lines.slice(start - 1, start - 1 + length).join("\n");
|
|
9866
|
+
const original = opts?.original;
|
|
9867
|
+
if (original !== void 0) {
|
|
9868
|
+
const first = original[0];
|
|
9869
|
+
for (let start = 1; start <= lastStart; start++) {
|
|
9870
|
+
if (lines[start - 1] !== first) continue;
|
|
9871
|
+
let same = true;
|
|
9872
|
+
for (let i = 1; i < length; i++) {
|
|
9873
|
+
if (lines[start - 1 + i] !== original[i]) {
|
|
9874
|
+
same = false;
|
|
9875
|
+
break;
|
|
9876
|
+
}
|
|
9877
|
+
}
|
|
9878
|
+
if (same && hashLines(joined(start), key) === pin) starts.push(start);
|
|
9879
|
+
}
|
|
9880
|
+
return { starts, truncated: false };
|
|
9881
|
+
}
|
|
9882
|
+
const budget = opts?.budget ?? MOVE_BUDGET_BYTES;
|
|
9883
|
+
const around = opts?.around ?? 1;
|
|
9884
|
+
const bandStart = Math.max(1, around - MOVE_WINDOW_LINES);
|
|
9885
|
+
const bandEnd = Math.min(lastStart, around + MOVE_WINDOW_LINES);
|
|
9886
|
+
const order = [];
|
|
9887
|
+
for (let start = bandStart; start <= bandEnd; start++) order.push(start);
|
|
9888
|
+
for (let start = 1; start < bandStart; start++) order.push(start);
|
|
9889
|
+
for (let start = bandEnd + 1; start <= lastStart; start++) order.push(start);
|
|
9890
|
+
let spent = 0;
|
|
9891
|
+
for (const start of order) {
|
|
9892
|
+
const text2 = joined(start);
|
|
9893
|
+
const bytes = Buffer.byteLength(text2, "utf8");
|
|
9894
|
+
if (spent + bytes > budget) return { starts, truncated: true };
|
|
9895
|
+
spent += bytes;
|
|
9896
|
+
if (hashLines(text2, key) === pin) starts.push(start);
|
|
9897
|
+
}
|
|
9898
|
+
return { starts, truncated: false };
|
|
9899
|
+
}
|
|
9900
|
+
var RANGE_SPEC = /^([1-9][0-9]*)(?:-([1-9][0-9]*))?$/;
|
|
9901
|
+
function parseLines(spec) {
|
|
9902
|
+
if (typeof spec === "number") {
|
|
9903
|
+
return Number.isInteger(spec) && spec >= 1 ? { start: spec, end: spec } : void 0;
|
|
9904
|
+
}
|
|
9905
|
+
const m = RANGE_SPEC.exec(spec);
|
|
9906
|
+
const startText = m?.[1];
|
|
9907
|
+
if (startText === void 0) return void 0;
|
|
9908
|
+
const start = Number(startText);
|
|
9909
|
+
const endText = m?.[2];
|
|
9910
|
+
const end = endText === void 0 ? start : Number(endText);
|
|
9911
|
+
return end < start ? void 0 : { start, end };
|
|
9912
|
+
}
|
|
9913
|
+
function lineSpec(lines) {
|
|
9914
|
+
return lines.start === lines.end ? lines.start : `${String(lines.start)}-${String(lines.end)}`;
|
|
9915
|
+
}
|
|
9916
|
+
function spellLines(lines) {
|
|
9917
|
+
return String(lineSpec(lines));
|
|
9918
|
+
}
|
|
9919
|
+
function toFileLines(lines, bodyLine) {
|
|
9920
|
+
return { start: bodyLine + lines.start - 1, end: bodyLine + lines.end - 1 };
|
|
9921
|
+
}
|
|
9922
|
+
function toBodyLines(lines, bodyLine) {
|
|
9923
|
+
return { start: lines.start - bodyLine + 1, end: lines.end - bodyLine + 1 };
|
|
9924
|
+
}
|
|
9925
|
+
function splitPageArgument(arg) {
|
|
9926
|
+
const text2 = arg.startsWith(STDIN_LINES_MARKER) ? `-${arg.slice(STDIN_LINES_MARKER.length)}` : arg;
|
|
9927
|
+
const m = /:([1-9][0-9]*)(?:-([1-9][0-9]*))?$/.exec(text2);
|
|
9928
|
+
const head = m === null ? text2 : text2.slice(0, m.index);
|
|
9929
|
+
if (m === null || head === "") return { page: arg };
|
|
9930
|
+
const start = Number(m[1]);
|
|
9931
|
+
const end = m[2] === void 0 ? start : Number(m[2]);
|
|
9932
|
+
if (end < start) {
|
|
9933
|
+
throw new LineRangeError(`Invalid range "${arg}": end line ${end} is before start line ${start}.`);
|
|
9934
|
+
}
|
|
9935
|
+
return { page: head, lines: { start, end } };
|
|
9766
9936
|
}
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9937
|
+
|
|
9938
|
+
// src/meta/core/derive/provenance.ts
|
|
9939
|
+
import picomatch5 from "picomatch";
|
|
9940
|
+
var ZERO_SHA = "0000000000000000000000000000000000000000";
|
|
9941
|
+
var DEFAULT_MACHINES = ["*[bot]"];
|
|
9942
|
+
var PROVENANCE_POINTER = "/provenance";
|
|
9943
|
+
var PLAIN_PIN = /^sha256-[0-9a-f]{64}$/;
|
|
9944
|
+
function provenanceEntries(value) {
|
|
9945
|
+
if (!Array.isArray(value)) return [];
|
|
9946
|
+
const out = [];
|
|
9947
|
+
for (const item of value) {
|
|
9948
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) continue;
|
|
9949
|
+
const record = item;
|
|
9950
|
+
const machine = record["generated-by"];
|
|
9951
|
+
const lines = record.lines;
|
|
9952
|
+
const integrity = record.integrity;
|
|
9953
|
+
if (typeof machine !== "string" || machine === "") continue;
|
|
9954
|
+
if (typeof lines !== "string" && typeof lines !== "number") continue;
|
|
9955
|
+
if (parseLines(lines) === void 0) continue;
|
|
9956
|
+
if (typeof integrity !== "string" || !PLAIN_PIN.test(integrity)) continue;
|
|
9957
|
+
out.push({ ...record, "generated-by": machine, lines, integrity });
|
|
9778
9958
|
}
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9959
|
+
return out;
|
|
9960
|
+
}
|
|
9961
|
+
function readProvenancePage(content, opts) {
|
|
9962
|
+
const lines = splitLines(content);
|
|
9963
|
+
const loc = opts?.fenced === false ? null : locateFrontmatter(content);
|
|
9964
|
+
if (loc === null) return { lines, bodyLine: 1, body: lines, stamp: [] };
|
|
9965
|
+
const head = content.slice(0, loc.closeEnd);
|
|
9966
|
+
const newlines = head.split("\n").length - 1;
|
|
9967
|
+
const bodyLine = newlines + (head.endsWith("\n") ? 1 : 2);
|
|
9968
|
+
let stamp = [];
|
|
9969
|
+
try {
|
|
9970
|
+
stamp = provenanceEntries(extractFrontmatter(content, "markdown").data.provenance);
|
|
9971
|
+
} catch {
|
|
9972
|
+
stamp = [];
|
|
9973
|
+
}
|
|
9974
|
+
return { lines, bodyLine, body: lines.slice(bodyLine - 1), stamp };
|
|
9975
|
+
}
|
|
9976
|
+
var HEADER = /^([0-9a-f]{40}(?:[0-9a-f]{24})?) ([1-9][0-9]*) ([1-9][0-9]*)(?: ([1-9][0-9]*))?$/;
|
|
9977
|
+
var ALL_ZERO = /^0+$/;
|
|
9978
|
+
function parseLinePorcelain(text2) {
|
|
9979
|
+
const rows = text2.split("\n");
|
|
9980
|
+
if (rows.length > 0 && rows[rows.length - 1] === "") rows.pop();
|
|
9981
|
+
const out = [];
|
|
9982
|
+
let i = 0;
|
|
9983
|
+
while (i < rows.length) {
|
|
9984
|
+
const headerRow = rows[i] ?? "";
|
|
9985
|
+
const m = HEADER.exec(headerRow);
|
|
9986
|
+
const sha = m?.[1];
|
|
9987
|
+
const orig = m?.[2];
|
|
9988
|
+
const final = m?.[3];
|
|
9989
|
+
if (sha === void 0 || orig === void 0 || final === void 0) {
|
|
9990
|
+
throw new Error(`porcelain: expected a header at row ${i + 1}, got ${JSON.stringify(headerRow)}`);
|
|
9991
|
+
}
|
|
9992
|
+
const finalLine = Number(final);
|
|
9993
|
+
const count = m?.[4];
|
|
9994
|
+
const fields = /* @__PURE__ */ new Map();
|
|
9995
|
+
i++;
|
|
9996
|
+
for (let row = rows[i]; row !== void 0 && !row.startsWith(" "); row = rows[i]) {
|
|
9997
|
+
const space2 = row.indexOf(" ");
|
|
9998
|
+
if (space2 === -1) fields.set(row, true);
|
|
9999
|
+
else fields.set(row.slice(0, space2), row.slice(space2 + 1));
|
|
10000
|
+
i++;
|
|
9790
10001
|
}
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
10002
|
+
const contentRow = rows[i];
|
|
10003
|
+
if (contentRow === void 0) throw new Error(`porcelain: line ${finalLine} has no content row`);
|
|
10004
|
+
const author = fields.get("author");
|
|
10005
|
+
const filename = fields.get("filename");
|
|
10006
|
+
if (typeof author !== "string" || typeof filename !== "string") {
|
|
10007
|
+
throw new Error(`porcelain: line ${finalLine} has no full header; run blame with --line-porcelain`);
|
|
9794
10008
|
}
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
10009
|
+
const mail = fields.get("author-mail");
|
|
10010
|
+
const previous = fields.get("previous");
|
|
10011
|
+
const space = typeof previous === "string" ? previous.indexOf(" ") : -1;
|
|
10012
|
+
out.push({
|
|
10013
|
+
sha,
|
|
10014
|
+
origLine: Number(orig),
|
|
10015
|
+
finalLine,
|
|
10016
|
+
...count !== void 0 ? { groupCount: Number(count) } : {},
|
|
10017
|
+
author,
|
|
10018
|
+
authorMail: typeof mail === "string" ? mail.replace(/^<(.*)>$/, "$1") : "",
|
|
10019
|
+
filename,
|
|
10020
|
+
...typeof previous === "string" && space !== -1 ? { previous: { sha: previous.slice(0, space), filename: previous.slice(space + 1) } } : {},
|
|
10021
|
+
boundary: fields.get("boundary") === true,
|
|
10022
|
+
uncommitted: ALL_ZERO.test(sha),
|
|
10023
|
+
content: contentRow.slice(1)
|
|
10024
|
+
});
|
|
10025
|
+
i++;
|
|
10026
|
+
}
|
|
10027
|
+
return out;
|
|
10028
|
+
}
|
|
10029
|
+
var IDENTITY_GLOB = { literalBrackets: true, dot: true, bash: true };
|
|
10030
|
+
function machineIdentity(value, machines) {
|
|
10031
|
+
const m = /^(.*?)\s*<([^>]*)>\s*$/.exec(value);
|
|
10032
|
+
const name2 = m === null ? value.trim() : m[1] ?? "";
|
|
10033
|
+
const email = m === null ? "" : m[2] ?? "";
|
|
10034
|
+
const match = (s) => s !== "" && machines.some((glob) => picomatch5.isMatch(s, glob, IDENTITY_GLOB));
|
|
10035
|
+
if (!match(name2) && !match(email)) return void 0;
|
|
10036
|
+
return name2 !== "" ? name2 : email;
|
|
10037
|
+
}
|
|
10038
|
+
function stampEvidence(commit, origLine, fenced) {
|
|
10039
|
+
if (commit.blob === void 0) return void 0;
|
|
10040
|
+
const page = readProvenancePage(commit.blob, fenced === void 0 ? void 0 : { fenced });
|
|
10041
|
+
const body = origLine - page.bodyLine + 1;
|
|
10042
|
+
if (body < 1) return void 0;
|
|
10043
|
+
for (const entry of commit.stamp === void 0 ? page.stamp : provenanceEntries(commit.stamp)) {
|
|
10044
|
+
const span = parseLines(entry.lines);
|
|
10045
|
+
if (span === void 0 || body < span.start || body > span.end) continue;
|
|
10046
|
+
if (pinOfLines(page.body, span) === entry.integrity) return entry["generated-by"];
|
|
10047
|
+
}
|
|
10048
|
+
return void 0;
|
|
10049
|
+
}
|
|
10050
|
+
function recordedEvidence(line, ctx) {
|
|
10051
|
+
if (line.uncommitted) return { rule: 5, sha: line.sha };
|
|
10052
|
+
const commit = ctx.commits.get(line.sha);
|
|
10053
|
+
if (commit === void 0) throw new Error(`no commit ${line.sha}`);
|
|
10054
|
+
const stamped = stampEvidence(commit, line.origLine, ctx.fenced);
|
|
10055
|
+
if (stamped !== void 0) return { machine: stamped, rule: 2, sha: line.sha };
|
|
10056
|
+
const generated = commit.trailers.generatedBy.map((v) => v.trim()).find((v) => v !== "");
|
|
10057
|
+
if (generated !== void 0) return { machine: generated, rule: 3, sha: line.sha };
|
|
10058
|
+
for (const value of commit.trailers.coAuthoredBy) {
|
|
10059
|
+
const machine = machineIdentity(value.trim(), ctx.machines);
|
|
10060
|
+
if (machine !== void 0) return { machine, rule: 4, sha: line.sha };
|
|
10061
|
+
}
|
|
10062
|
+
return { rule: 5, sha: line.sha };
|
|
10063
|
+
}
|
|
10064
|
+
function resolveLineEvidence(line, ctx) {
|
|
10065
|
+
if (line.uncommitted && ctx.generatedBy !== void 0 && ctx.generatedBy !== "") {
|
|
10066
|
+
return { machine: ctx.generatedBy, rule: 1, sha: line.sha };
|
|
10067
|
+
}
|
|
10068
|
+
return recordedEvidence(line, ctx);
|
|
10069
|
+
}
|
|
10070
|
+
function pinOf(lines, span) {
|
|
10071
|
+
const pin = pinOfLines(lines, span);
|
|
10072
|
+
if (pin === void 0) throw new Error(`provenance: lines ${spellLines(span)} fall outside the body`);
|
|
10073
|
+
return pin;
|
|
10074
|
+
}
|
|
10075
|
+
function contextOf(input) {
|
|
10076
|
+
return {
|
|
10077
|
+
commits: input.commits,
|
|
10078
|
+
machines: input.machines ?? DEFAULT_MACHINES,
|
|
10079
|
+
...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {},
|
|
10080
|
+
...input.fenced !== void 0 ? { fenced: input.fenced } : {}
|
|
10081
|
+
};
|
|
10082
|
+
}
|
|
10083
|
+
function readOptions(fenced) {
|
|
10084
|
+
return fenced === void 0 ? void 0 : { fenced };
|
|
10085
|
+
}
|
|
10086
|
+
function deriveProvenance(input) {
|
|
10087
|
+
const page = readProvenancePage(input.content, readOptions(input.fenced));
|
|
10088
|
+
const ctx = contextOf(input);
|
|
10089
|
+
const evidenceByLine = /* @__PURE__ */ new Map();
|
|
10090
|
+
for (const line of input.blame) {
|
|
10091
|
+
const body = line.finalLine - page.bodyLine + 1;
|
|
10092
|
+
if (body < 1) continue;
|
|
10093
|
+
evidenceByLine.set(body, resolveLineEvidence(line, ctx));
|
|
10094
|
+
}
|
|
10095
|
+
const groups = [];
|
|
10096
|
+
let open3;
|
|
10097
|
+
for (let n = 1; n <= page.body.length; n++) {
|
|
10098
|
+
const evidence = evidenceByLine.get(n);
|
|
10099
|
+
const machine = evidence?.machine;
|
|
10100
|
+
if (open3 !== void 0 && machine === open3.machine && n === open3.end + 1) {
|
|
10101
|
+
open3.end = n;
|
|
10102
|
+
continue;
|
|
10103
|
+
}
|
|
10104
|
+
if (open3 !== void 0) groups.push(open3);
|
|
10105
|
+
open3 = machine === void 0 || evidence === void 0 ? void 0 : { machine, start: n, end: n, first: evidence };
|
|
10106
|
+
}
|
|
10107
|
+
if (open3 !== void 0) groups.push(open3);
|
|
10108
|
+
return {
|
|
10109
|
+
page,
|
|
10110
|
+
evidenceByLine,
|
|
10111
|
+
derived: groups.map((g) => {
|
|
10112
|
+
const span = { start: g.start, end: g.end };
|
|
10113
|
+
return {
|
|
10114
|
+
entry: { "generated-by": g.machine, lines: lineSpec(span), integrity: pinOf(page.body, span) },
|
|
10115
|
+
span,
|
|
10116
|
+
evidence: g.first
|
|
10117
|
+
};
|
|
10118
|
+
})
|
|
10119
|
+
};
|
|
10120
|
+
}
|
|
10121
|
+
function nearest(candidates, recorded) {
|
|
10122
|
+
let best;
|
|
10123
|
+
for (const c of candidates) {
|
|
10124
|
+
const d = Math.abs(c.span.start - recorded.start);
|
|
10125
|
+
if (best === void 0 || d < best.d || d === best.d && c.span.start < best.c.span.start) best = { c, d };
|
|
10126
|
+
}
|
|
10127
|
+
return best?.c;
|
|
10128
|
+
}
|
|
10129
|
+
var sameSpan = (a, b) => a.start === b.start && a.end === b.end;
|
|
10130
|
+
function scanEvidence(evidenceByLine, span, machine) {
|
|
10131
|
+
let anyMachine = false;
|
|
10132
|
+
for (let n = span.start; n <= span.end; n++) {
|
|
10133
|
+
const evidence = evidenceByLine.get(n);
|
|
10134
|
+
if (evidence?.machine === void 0) continue;
|
|
10135
|
+
anyMachine = true;
|
|
10136
|
+
if (evidence.machine !== machine) return { contradiction: evidence, anyMachine };
|
|
10137
|
+
}
|
|
10138
|
+
return { anyMachine };
|
|
10139
|
+
}
|
|
10140
|
+
function compareProvenance(stamped, derivation) {
|
|
10141
|
+
const { page, evidenceByLine, derived } = derivation;
|
|
10142
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
10143
|
+
const results = [];
|
|
10144
|
+
for (const entry of provenanceEntries(stamped)) {
|
|
10145
|
+
const recorded = parseLines(entry.lines);
|
|
10146
|
+
if (recorded === void 0) continue;
|
|
10147
|
+
const machine = entry["generated-by"];
|
|
10148
|
+
const takenHere = [...claimed].some((d) => sameSpan(d.span, recorded));
|
|
10149
|
+
if (!takenHere && pinOfLines(page.body, recorded) === entry.integrity) {
|
|
10150
|
+
const here = scanEvidence(evidenceByLine, recorded, machine);
|
|
10151
|
+
if (here.contradiction === void 0) {
|
|
10152
|
+
const exact = derived.find(
|
|
10153
|
+
(d) => !claimed.has(d) && sameSpan(d.span, recorded) && d.entry.integrity === entry.integrity
|
|
10154
|
+
);
|
|
10155
|
+
if (exact !== void 0) claimed.add(exact);
|
|
10156
|
+
results.push({ status: "current", entry, span: recorded, ...here.anyMachine ? {} : { noEvidence: true } });
|
|
10157
|
+
continue;
|
|
10158
|
+
}
|
|
10159
|
+
}
|
|
10160
|
+
const pool = derived.filter((d) => !claimed.has(d) && d.entry.integrity === entry.integrity);
|
|
10161
|
+
const hit = nearest(pool, recorded);
|
|
10162
|
+
if (hit !== void 0) {
|
|
10163
|
+
claimed.add(hit);
|
|
10164
|
+
if (hit.entry["generated-by"] !== machine) {
|
|
10165
|
+
results.push({ status: "stale", entry, span: hit.span, evidence: hit.evidence });
|
|
10166
|
+
} else {
|
|
10167
|
+
results.push({ status: sameSpan(hit.span, recorded) ? "current" : "moved", entry, span: hit.span });
|
|
10168
|
+
}
|
|
10169
|
+
continue;
|
|
10170
|
+
}
|
|
10171
|
+
const width = recorded.end - recorded.start + 1;
|
|
10172
|
+
const claimedSpans = [...claimed].map((d) => d.span);
|
|
10173
|
+
const windows = findWindows(page.body, width, entry.integrity, void 0, { around: recorded.start, budget: Infinity }).starts.map((start) => ({ span: { start, end: start + width - 1 } })).filter((w) => !claimedSpans.some((s) => sameSpan(s, w.span)));
|
|
10174
|
+
const found = nearest(windows, recorded);
|
|
10175
|
+
if (found === void 0) {
|
|
10176
|
+
results.push({ status: "changed", entry, span: recorded });
|
|
10177
|
+
continue;
|
|
10178
|
+
}
|
|
10179
|
+
const { contradiction, anyMachine } = scanEvidence(evidenceByLine, found.span, machine);
|
|
10180
|
+
if (contradiction !== void 0) {
|
|
10181
|
+
results.push({ status: "stale", entry, span: found.span, evidence: contradiction });
|
|
10182
|
+
continue;
|
|
10183
|
+
}
|
|
10184
|
+
results.push({
|
|
10185
|
+
status: sameSpan(found.span, recorded) ? "current" : "moved",
|
|
10186
|
+
entry,
|
|
10187
|
+
span: found.span,
|
|
10188
|
+
...anyMachine ? {} : { noEvidence: true }
|
|
10189
|
+
});
|
|
10190
|
+
}
|
|
10191
|
+
const covering = results.map((r) => r.span);
|
|
10192
|
+
for (const d of derived) {
|
|
10193
|
+
if (claimed.has(d)) continue;
|
|
10194
|
+
let uncovered = false;
|
|
10195
|
+
for (let n = d.span.start; n <= d.span.end && !uncovered; n++) {
|
|
10196
|
+
uncovered = !covering.some((s) => n >= s.start && n <= s.end);
|
|
10197
|
+
}
|
|
10198
|
+
if (uncovered) results.push({ status: "unset", entry: d.entry, span: d.span, evidence: d.evidence });
|
|
10199
|
+
}
|
|
10200
|
+
return results;
|
|
10201
|
+
}
|
|
10202
|
+
function planProvenanceWrite(comparisons, derivation) {
|
|
10203
|
+
const kept = [];
|
|
10204
|
+
for (const r of comparisons) {
|
|
10205
|
+
if (r.status === "current") kept.push({ entry: r.entry, start: r.span.start });
|
|
10206
|
+
else if (r.status === "moved") kept.push({ entry: { ...r.entry, lines: lineSpec(r.span) }, start: r.span.start });
|
|
10207
|
+
}
|
|
10208
|
+
const same = (a, b) => a["generated-by"] === b["generated-by"] && String(a.lines) === String(b.lines) && a.integrity === b.integrity;
|
|
10209
|
+
const out = [...kept];
|
|
10210
|
+
for (const d of derivation.derived) {
|
|
10211
|
+
if (!kept.some((k) => same(k.entry, d.entry))) out.push({ entry: d.entry, start: d.span.start });
|
|
10212
|
+
}
|
|
10213
|
+
return out.sort((a, b) => a.start - b.start).map((k) => k.entry);
|
|
10214
|
+
}
|
|
10215
|
+
var short = (sha) => sha.slice(0, 7);
|
|
10216
|
+
function fileSpelling(span, bodyLine) {
|
|
10217
|
+
return spellLines(toFileLines(span, bodyLine));
|
|
10218
|
+
}
|
|
10219
|
+
function evidenceText(evidence) {
|
|
10220
|
+
if (evidence?.machine === void 0) return "pin";
|
|
10221
|
+
return evidence.rule === 1 ? "uncommitted" : `blame ${short(evidence.sha)}`;
|
|
10222
|
+
}
|
|
10223
|
+
function evidenceRef(evidence) {
|
|
10224
|
+
if (evidence === void 0) return "no evidence";
|
|
10225
|
+
return evidence.rule === 1 ? "uncommitted" : short(evidence.sha);
|
|
10226
|
+
}
|
|
10227
|
+
function rangeResults(comparisons, bodyLine) {
|
|
10228
|
+
return comparisons.map((r) => {
|
|
10229
|
+
const lines = fileSpelling(r.span, bodyLine);
|
|
10230
|
+
const recorded = parseLines(r.entry.lines);
|
|
10231
|
+
const from = recorded === void 0 ? void 0 : fileSpelling(recorded, bodyLine);
|
|
10232
|
+
const machine = r.entry["generated-by"];
|
|
10233
|
+
switch (r.status) {
|
|
10234
|
+
case "stale":
|
|
10235
|
+
return {
|
|
10236
|
+
lines,
|
|
10237
|
+
"generated-by": r.evidence?.machine ?? machine,
|
|
10238
|
+
integrity: r.entry.integrity,
|
|
10239
|
+
status: r.status,
|
|
10240
|
+
evidence: evidenceText(r.evidence),
|
|
10241
|
+
...from !== void 0 ? { from: { lines: from, "generated-by": machine } } : {},
|
|
10242
|
+
written: false
|
|
10243
|
+
};
|
|
10244
|
+
case "moved":
|
|
10245
|
+
return {
|
|
10246
|
+
lines,
|
|
10247
|
+
"generated-by": machine,
|
|
10248
|
+
integrity: r.entry.integrity,
|
|
10249
|
+
status: r.status,
|
|
10250
|
+
evidence: "pin",
|
|
10251
|
+
...from !== void 0 ? { from: { lines: from, "generated-by": machine } } : {},
|
|
10252
|
+
written: false
|
|
10253
|
+
};
|
|
10254
|
+
case "unset":
|
|
10255
|
+
return { lines, "generated-by": machine, integrity: r.entry.integrity, status: r.status, evidence: evidenceText(r.evidence), written: false };
|
|
10256
|
+
case "current":
|
|
10257
|
+
case "changed":
|
|
10258
|
+
return { lines, "generated-by": machine, integrity: r.entry.integrity, status: r.status, evidence: "pin", written: false };
|
|
10259
|
+
}
|
|
10260
|
+
});
|
|
10261
|
+
}
|
|
10262
|
+
function provenanceFindings(comparisons, bodyLine) {
|
|
10263
|
+
const findings = [];
|
|
10264
|
+
for (const r of comparisons) {
|
|
10265
|
+
const machine = r.entry["generated-by"];
|
|
10266
|
+
const lines = fileSpelling(r.span, bodyLine);
|
|
10267
|
+
let message;
|
|
10268
|
+
switch (r.status) {
|
|
10269
|
+
case "changed":
|
|
10270
|
+
message = `provenance lines ${lines} changed since ${machine} wrote them`;
|
|
10271
|
+
break;
|
|
10272
|
+
case "stale":
|
|
10273
|
+
message = `provenance lines ${lines} say ${machine}; blame says ${r.evidence?.machine ?? "another machine"} (${evidenceRef(r.evidence)})`;
|
|
10274
|
+
break;
|
|
10275
|
+
case "unset":
|
|
10276
|
+
message = `provenance is unset for lines ${lines}; blame says ${machine} (${evidenceRef(r.evidence)})`;
|
|
10277
|
+
break;
|
|
10278
|
+
case "current":
|
|
10279
|
+
case "moved":
|
|
10280
|
+
continue;
|
|
10281
|
+
}
|
|
10282
|
+
findings.push({
|
|
10283
|
+
schema: DERIVED_STALE_SCHEMA,
|
|
10284
|
+
keyword: DERIVED_KEYWORD,
|
|
10285
|
+
subject: `provenance ${r.entry.integrity}`,
|
|
10286
|
+
instancePath: PROVENANCE_POINTER,
|
|
10287
|
+
message: `${message} \u2014 run manni meta derive`,
|
|
10288
|
+
line: toFileLines(r.span, bodyLine).start
|
|
10289
|
+
});
|
|
10290
|
+
}
|
|
10291
|
+
return findings;
|
|
10292
|
+
}
|
|
10293
|
+
function renderProvenanceValue(entries, bodyLine) {
|
|
10294
|
+
return entries.map((e) => {
|
|
10295
|
+
const span = parseLines(e.lines);
|
|
10296
|
+
const lines = span === void 0 ? String(e.lines) : fileSpelling(span, bodyLine);
|
|
10297
|
+
return `lines ${lines} ${e["generated-by"]}`;
|
|
10298
|
+
}).join("; ");
|
|
10299
|
+
}
|
|
10300
|
+
function parseProvenanceTarget(arg) {
|
|
10301
|
+
try {
|
|
10302
|
+
return splitPageArgument(arg);
|
|
10303
|
+
} catch (err) {
|
|
10304
|
+
if (err instanceof LineRangeError) throw new DocmetaError(`${arg} ends before it starts.`);
|
|
10305
|
+
throw err;
|
|
10306
|
+
}
|
|
10307
|
+
}
|
|
10308
|
+
function attributeRange(input) {
|
|
10309
|
+
const { target } = input;
|
|
10310
|
+
const { page: path, lines: range } = parseProvenanceTarget(target);
|
|
10311
|
+
if (range === void 0) throw new Error(`provenance: ${target} names no lines`);
|
|
10312
|
+
const page = readProvenancePage(input.content, readOptions(input.fenced));
|
|
10313
|
+
if (range.end > page.lines.length) {
|
|
10314
|
+
throw new DocmetaError(
|
|
10315
|
+
`${path} has no lines ${spellLines(range)}: the file ends at line ${page.lines.length}.`
|
|
10316
|
+
);
|
|
10317
|
+
}
|
|
10318
|
+
if (range.start < page.bodyLine) {
|
|
10319
|
+
throw new DocmetaError(
|
|
10320
|
+
`${target} reaches into the frontmatter; provenance pins body lines, which start at line ${page.bodyLine}.`
|
|
10321
|
+
);
|
|
10322
|
+
}
|
|
10323
|
+
const ctx = contextOf(input);
|
|
10324
|
+
for (const line of input.blame) {
|
|
10325
|
+
if (line.finalLine < range.start || line.finalLine > range.end) continue;
|
|
10326
|
+
const evidence = recordedEvidence(line, ctx);
|
|
10327
|
+
if (evidence.machine !== void 0 && evidence.machine !== input.generatedBy) {
|
|
10328
|
+
throw new DocmetaError(
|
|
10329
|
+
`${target}: blame attributes these lines to ${evidence.machine} (${short(evidence.sha)}); --generated-by cannot overrule a recorded machine.`
|
|
10330
|
+
);
|
|
10331
|
+
}
|
|
10332
|
+
}
|
|
10333
|
+
const span = toBodyLines(range, page.bodyLine);
|
|
10334
|
+
return { "generated-by": input.generatedBy, lines: lineSpec(span), integrity: pinOf(page.body, span) };
|
|
10335
|
+
}
|
|
10336
|
+
|
|
10337
|
+
// src/meta/core/derive/git.ts
|
|
10338
|
+
var DEFAULT_BULK_THRESHOLD = 32;
|
|
10339
|
+
var NULL_SHA = "0".repeat(40);
|
|
10340
|
+
var MAX_GIT_OUTPUT_BYTES = Math.min(
|
|
10341
|
+
512 * 1024 * 1024,
|
|
10342
|
+
bufferConstants.MAX_STRING_LENGTH
|
|
10343
|
+
);
|
|
10344
|
+
var HistoryTooLarge = class extends Error {
|
|
10345
|
+
};
|
|
10346
|
+
var BlobsUnreadable = class extends Error {
|
|
10347
|
+
constructor(detail) {
|
|
10348
|
+
super(detail);
|
|
10349
|
+
this.detail = detail;
|
|
10350
|
+
}
|
|
10351
|
+
detail;
|
|
10352
|
+
};
|
|
10353
|
+
var BlameUnreadable = class extends Error {
|
|
10354
|
+
constructor(rel, detail) {
|
|
10355
|
+
super(rel);
|
|
10356
|
+
this.rel = rel;
|
|
10357
|
+
this.detail = detail;
|
|
10358
|
+
}
|
|
10359
|
+
rel;
|
|
10360
|
+
detail;
|
|
10361
|
+
};
|
|
10362
|
+
function bodyOf(content, fenced) {
|
|
10363
|
+
if (fenced !== true) return content;
|
|
10364
|
+
const loc = locateFrontmatter(content);
|
|
10365
|
+
if (!loc) return content;
|
|
10366
|
+
return content.slice(0, loc.openStart) + content.slice(loc.closeEnd);
|
|
10367
|
+
}
|
|
10368
|
+
async function deriveFromGit(inputs, opts) {
|
|
10369
|
+
const records = /* @__PURE__ */ new Map();
|
|
10370
|
+
const byRoot = groupByRoot(inputs);
|
|
10371
|
+
if (byRoot.size === 0) {
|
|
10372
|
+
return {
|
|
10373
|
+
status: {
|
|
10374
|
+
available: false,
|
|
10375
|
+
reason: "no git repository contains the documents"
|
|
10376
|
+
},
|
|
10377
|
+
records
|
|
10378
|
+
};
|
|
10379
|
+
}
|
|
10380
|
+
const threshold = opts.bulkThreshold ?? DEFAULT_BULK_THRESHOLD;
|
|
10381
|
+
const machines = opts.machines ?? DEFAULT_MACHINES;
|
|
10382
|
+
const wantsProvenance = opts.fields?.includes(PROVENANCE_FIELD) ?? false;
|
|
10383
|
+
const maxBytes = opts.maxOutputBytes ?? MAX_GIT_OUTPUT_BYTES;
|
|
10384
|
+
const reasons = [];
|
|
10385
|
+
for (const [root, bucket] of byRoot) {
|
|
10386
|
+
const run2 = (args, stdin) => runGit(args, root, stdin, maxBytes);
|
|
10387
|
+
const shallow = await run2(["rev-parse", "--is-shallow-repository"]);
|
|
10388
|
+
if (shallow.missing) {
|
|
10389
|
+
return {
|
|
10390
|
+
status: { available: false, reason: "git is not on PATH" },
|
|
10391
|
+
records: /* @__PURE__ */ new Map()
|
|
10392
|
+
};
|
|
10393
|
+
}
|
|
10394
|
+
if (shallow.code !== 0) {
|
|
10395
|
+
reasons.push(`git could not read the repository at ${root}`);
|
|
10396
|
+
continue;
|
|
10397
|
+
}
|
|
10398
|
+
if (text(shallow).trim() === "true") {
|
|
10399
|
+
reasons.push(
|
|
10400
|
+
`this checkout is shallow; use actions/checkout with fetch-depth: 0 (${root})`
|
|
10401
|
+
);
|
|
9799
10402
|
continue;
|
|
9800
10403
|
}
|
|
9801
|
-
let histories;
|
|
9802
|
-
let blobs;
|
|
9803
10404
|
try {
|
|
9804
|
-
histories = bucket.length <= threshold ? await perFileHistories(run2, bucket) : await bulkHistories(run2, bucket);
|
|
10405
|
+
const histories = bucket.length <= threshold ? await perFileHistories(run2, bucket) : await bulkHistories(run2, bucket);
|
|
9805
10406
|
if (histories === null) {
|
|
9806
10407
|
reasons.push(`git could not read the history at ${root}`);
|
|
9807
10408
|
continue;
|
|
9808
10409
|
}
|
|
9809
|
-
blobs = await fetchBlobs(run2, neededBlobs(histories));
|
|
10410
|
+
const blobs = await fetchBlobs(run2, neededBlobs(histories));
|
|
10411
|
+
for (const entry of bucket) {
|
|
10412
|
+
const history = histories.get(entry.rel) ?? [];
|
|
10413
|
+
const facts = judge(entry.input, root, history, blobs, opts.now, machines);
|
|
10414
|
+
if (wantsProvenance) {
|
|
10415
|
+
const derived = await provenanceFor(run2, root, entry, history, blobs, {
|
|
10416
|
+
machines,
|
|
10417
|
+
...opts.generatedBy !== void 0 ? { generatedBy: opts.generatedBy } : {}
|
|
10418
|
+
});
|
|
10419
|
+
facts.provenance = derived.value;
|
|
10420
|
+
facts.provenanceDerivation = derived.derivation;
|
|
10421
|
+
}
|
|
10422
|
+
records.set(entry.input.label, facts);
|
|
10423
|
+
}
|
|
9810
10424
|
} catch (err) {
|
|
9811
10425
|
if (err instanceof BlobsUnreadable) {
|
|
9812
10426
|
reasons.push(`git cat-file could not read the history (${root}): ${err.detail}`);
|
|
9813
10427
|
continue;
|
|
9814
10428
|
}
|
|
10429
|
+
if (err instanceof BlameUnreadable) {
|
|
10430
|
+
const why = err.detail === "" ? "" : `: ${err.detail}`;
|
|
10431
|
+
reasons.push(`git blame could not read ${err.rel} (${root})${why}`);
|
|
10432
|
+
continue;
|
|
10433
|
+
}
|
|
9815
10434
|
if (!(err instanceof HistoryTooLarge)) throw err;
|
|
9816
10435
|
reasons.push(`git history is too large to read in one pass (${root})`);
|
|
9817
10436
|
continue;
|
|
9818
10437
|
}
|
|
9819
|
-
for (const entry of bucket) {
|
|
9820
|
-
const history = histories.get(entry.rel) ?? [];
|
|
9821
|
-
records.set(
|
|
9822
|
-
entry.input.label,
|
|
9823
|
-
judge(entry.input, root, history, blobs, opts.now)
|
|
9824
|
-
);
|
|
9825
|
-
}
|
|
9826
10438
|
}
|
|
9827
10439
|
if (reasons.length > 0) {
|
|
9828
10440
|
return {
|
|
@@ -9850,7 +10462,7 @@ function groupByRoot(inputs) {
|
|
|
9850
10462
|
}
|
|
9851
10463
|
return byRoot;
|
|
9852
10464
|
}
|
|
9853
|
-
var RECORD_FORMAT = "--format=%x1e%H%x00%aI%x00%an%x00%ae%x00%(trailers:key=Co-authored-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Reviewed-by,valueonly,unfold,separator=%x1f)";
|
|
10465
|
+
var RECORD_FORMAT = "--format=%x1e%H%x00%aI%x00%an%x00%ae%x00%(trailers:key=Co-authored-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Reviewed-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Generated-by,valueonly,unfold,separator=%x1f)";
|
|
9854
10466
|
var LOG_ARGS = [
|
|
9855
10467
|
"-c",
|
|
9856
10468
|
"core.quotePath=false",
|
|
@@ -9916,6 +10528,7 @@ function parseLog(text2) {
|
|
|
9916
10528
|
authorEmail: fields[3] ?? "",
|
|
9917
10529
|
coAuthors: splitTrailers(fields[4]),
|
|
9918
10530
|
reviewedBy: splitTrailers(fields[5]),
|
|
10531
|
+
generatedBy: splitTrailers(fields[6]),
|
|
9919
10532
|
raw
|
|
9920
10533
|
});
|
|
9921
10534
|
}
|
|
@@ -10006,6 +10619,7 @@ function attribute(records, wanted) {
|
|
|
10006
10619
|
authorEmail: record.authorEmail,
|
|
10007
10620
|
coAuthors: record.coAuthors,
|
|
10008
10621
|
reviewedBy: record.reviewedBy,
|
|
10622
|
+
generatedBy: record.generatedBy,
|
|
10009
10623
|
oldBlob: entry.oldBlob === NULL_SHA ? null : entry.oldBlob,
|
|
10010
10624
|
newBlob: entry.newBlob === NULL_SHA ? null : entry.newBlob,
|
|
10011
10625
|
pathAtCommit: entry.newPath
|
|
@@ -10055,7 +10669,7 @@ var git = (value, evidence) => ({
|
|
|
10055
10669
|
source: "git",
|
|
10056
10670
|
evidence
|
|
10057
10671
|
});
|
|
10058
|
-
function judge(input, root, history, blobs, now) {
|
|
10672
|
+
function judge(input, root, history, blobs, now, machines) {
|
|
10059
10673
|
const fenced = input.extracted.fenced;
|
|
10060
10674
|
const extractor = extractorFor(input);
|
|
10061
10675
|
const blobBody = (sha) => sha === null ? null : bodyOf(blobs.get(sha) ?? "", fenced);
|
|
@@ -10092,11 +10706,11 @@ function judge(input, root, history, blobs, now) {
|
|
|
10092
10706
|
stampsOf(newestBody.oldBlob)
|
|
10093
10707
|
);
|
|
10094
10708
|
}
|
|
10095
|
-
const authors = bodyChanging.length === 0 ? null : authorsOf(bodyChanging);
|
|
10709
|
+
const authors = bodyChanging.length === 0 ? null : authorsOf(bodyChanging, machines);
|
|
10096
10710
|
let reviewedBy = null;
|
|
10097
10711
|
let lastReviewed = null;
|
|
10098
10712
|
if (!workingChanged && newestBody !== void 0 && newestBody.reviewedBy.length > 0) {
|
|
10099
|
-
const evidence = `Reviewed-by trailer in ${
|
|
10713
|
+
const evidence = `Reviewed-by trailer in ${short2(newestBody.sha)}`;
|
|
10100
10714
|
reviewedBy = git(newestBody.reviewedBy.map(nameOf), evidence);
|
|
10101
10715
|
lastReviewed = git(datePart(newestBody.authorDate), evidence);
|
|
10102
10716
|
}
|
|
@@ -10106,6 +10720,7 @@ function judge(input, root, history, blobs, now) {
|
|
|
10106
10720
|
authors,
|
|
10107
10721
|
"reviewed-by": reviewedBy,
|
|
10108
10722
|
"last-reviewed": lastReviewed,
|
|
10723
|
+
provenance: null,
|
|
10109
10724
|
lastBodyCommit,
|
|
10110
10725
|
root
|
|
10111
10726
|
};
|
|
@@ -10113,24 +10728,26 @@ function judge(input, root, history, blobs, now) {
|
|
|
10113
10728
|
function createdOf(oldest, stamps) {
|
|
10114
10729
|
const stamp = stamps["created"];
|
|
10115
10730
|
if (stamp !== void 0 && stamp !== null) {
|
|
10116
|
-
return git(stamp, `stamped in ${
|
|
10731
|
+
return git(stamp, `stamped in ${short2(oldest.sha)}`);
|
|
10117
10732
|
}
|
|
10118
10733
|
const date = datePart(oldest.authorDate);
|
|
10119
|
-
return git(date, `added in ${
|
|
10734
|
+
return git(date, `added in ${short2(oldest.sha)} (${date})`);
|
|
10120
10735
|
}
|
|
10121
10736
|
function lastUpdatedOf(commit, after, before) {
|
|
10122
10737
|
const stamp = after["last-updated"];
|
|
10123
10738
|
if (stamp !== void 0 && stamp !== null && !sameValue(stamp, before["last-updated"])) {
|
|
10124
|
-
return git(stamp, `stamped in ${
|
|
10739
|
+
return git(stamp, `stamped in ${short2(commit.sha)}`);
|
|
10125
10740
|
}
|
|
10126
10741
|
const date = datePart(commit.authorDate);
|
|
10127
|
-
return git(date, `body changed in ${
|
|
10742
|
+
return git(date, `body changed in ${short2(commit.sha)} (${date})`);
|
|
10128
10743
|
}
|
|
10129
|
-
function authorsOf(bodyChanging) {
|
|
10744
|
+
function authorsOf(bodyChanging, machines) {
|
|
10130
10745
|
const seen = /* @__PURE__ */ new Set();
|
|
10131
10746
|
const names = [];
|
|
10132
10747
|
const add = (name2, email) => {
|
|
10133
|
-
if (name2 === ""
|
|
10748
|
+
if (name2 === "") return;
|
|
10749
|
+
const identity = email === null || email === "" ? name2 : `${name2} <${email}>`;
|
|
10750
|
+
if (machineIdentity(identity, machines) !== void 0) return;
|
|
10134
10751
|
const key = email === null || email === "" ? `name:${name2.toLowerCase()}` : email.toLowerCase();
|
|
10135
10752
|
if (seen.has(key)) return;
|
|
10136
10753
|
seen.add(key);
|
|
@@ -10143,6 +10760,204 @@ function authorsOf(bodyChanging) {
|
|
|
10143
10760
|
const n = bodyChanging.length;
|
|
10144
10761
|
return git(names, `${n} body-changing ${n === 1 ? "commit" : "commits"}`);
|
|
10145
10762
|
}
|
|
10763
|
+
function provenanceFenced(extracted) {
|
|
10764
|
+
return extracted.fenced === true || extracted.format === "markdown" || extracted.format === "mdx";
|
|
10765
|
+
}
|
|
10766
|
+
var OUTSIDE_RANGE = "outside-the-attributed-range";
|
|
10767
|
+
var ATTRIBUTED = ":attributed";
|
|
10768
|
+
async function provenanceFor(run2, root, entry, history, blobs, opts) {
|
|
10769
|
+
const { input, rel } = entry;
|
|
10770
|
+
const fenced = provenanceFenced(input.extracted);
|
|
10771
|
+
const blame = await blameOf(run2, rel, input.content);
|
|
10772
|
+
const commits = await commitEvidence(run2, root, entry, blame, history, blobs, input.provenanceManifest);
|
|
10773
|
+
const base = { content: input.content, commits, machines: opts.machines, fenced };
|
|
10774
|
+
let derivation;
|
|
10775
|
+
const attribution = input.attribution;
|
|
10776
|
+
if (attribution !== void 0) {
|
|
10777
|
+
const ranges = attribution.targets.flatMap((target) => {
|
|
10778
|
+
attributeRange({ ...base, blame, target, generatedBy: attribution.generatedBy });
|
|
10779
|
+
const range = parseProvenanceTarget(target).lines;
|
|
10780
|
+
return range === void 0 ? [] : [range];
|
|
10781
|
+
});
|
|
10782
|
+
const inRange = (line) => ranges.some((range) => line.finalLine >= range.start && line.finalLine <= range.end);
|
|
10783
|
+
const attributed = new Map(commits);
|
|
10784
|
+
const trailers = { generatedBy: [attribution.generatedBy], coAuthoredBy: [] };
|
|
10785
|
+
attributed.set(OUTSIDE_RANGE, { sha: OUTSIDE_RANGE, trailers: { generatedBy: [], coAuthoredBy: [] } });
|
|
10786
|
+
const rekeyed = blame.map((line) => {
|
|
10787
|
+
if (inRange(line)) {
|
|
10788
|
+
if (line.uncommitted) return line;
|
|
10789
|
+
const sha = `${line.sha}${ATTRIBUTED}`;
|
|
10790
|
+
attributed.set(sha, { sha, trailers });
|
|
10791
|
+
return { ...line, sha };
|
|
10792
|
+
}
|
|
10793
|
+
return line.uncommitted ? { ...line, sha: OUTSIDE_RANGE, uncommitted: false } : line;
|
|
10794
|
+
});
|
|
10795
|
+
derivation = deriveProvenance({
|
|
10796
|
+
...base,
|
|
10797
|
+
commits: attributed,
|
|
10798
|
+
generatedBy: attribution.generatedBy,
|
|
10799
|
+
blame: rekeyed
|
|
10800
|
+
});
|
|
10801
|
+
} else {
|
|
10802
|
+
derivation = deriveProvenance({
|
|
10803
|
+
...base,
|
|
10804
|
+
blame,
|
|
10805
|
+
...opts.generatedBy !== void 0 ? { generatedBy: opts.generatedBy } : {}
|
|
10806
|
+
});
|
|
10807
|
+
}
|
|
10808
|
+
const entries = derivation.derived.map((d) => d.entry);
|
|
10809
|
+
return {
|
|
10810
|
+
value: entries.length === 0 ? null : git(entries, provenanceEvidence(derivation)),
|
|
10811
|
+
derivation
|
|
10812
|
+
};
|
|
10813
|
+
}
|
|
10814
|
+
function provenanceEvidence(derivation) {
|
|
10815
|
+
const shas = [];
|
|
10816
|
+
let uncommitted = false;
|
|
10817
|
+
for (const range of derivation.derived) {
|
|
10818
|
+
for (let n = range.span.start; n <= range.span.end; n++) {
|
|
10819
|
+
const evidence = derivation.evidenceByLine.get(n);
|
|
10820
|
+
if (evidence?.machine === void 0) continue;
|
|
10821
|
+
if (evidence.rule === 1) uncommitted = true;
|
|
10822
|
+
else if (!shas.includes(evidence.sha)) shas.push(evidence.sha);
|
|
10823
|
+
}
|
|
10824
|
+
}
|
|
10825
|
+
const first = shas[0];
|
|
10826
|
+
if (first === void 0) return "uncommitted";
|
|
10827
|
+
const parts = [`blame ${short2(first)}`];
|
|
10828
|
+
if (shas.length > 1) parts.push(`${String(shas.length)} commits`);
|
|
10829
|
+
if (uncommitted) parts.push("uncommitted");
|
|
10830
|
+
return parts.join(", ");
|
|
10831
|
+
}
|
|
10832
|
+
async function blameOf(run2, rel, content) {
|
|
10833
|
+
const out = await run2([
|
|
10834
|
+
"-c",
|
|
10835
|
+
"core.quotePath=false",
|
|
10836
|
+
"blame",
|
|
10837
|
+
"--no-ignore-revs-file",
|
|
10838
|
+
"--line-porcelain",
|
|
10839
|
+
"--",
|
|
10840
|
+
rel
|
|
10841
|
+
]);
|
|
10842
|
+
if (out.tooLarge) throw new HistoryTooLarge();
|
|
10843
|
+
if (out.code !== 0) {
|
|
10844
|
+
const inHead = await run2(["cat-file", "-e", `HEAD:${rel}`]);
|
|
10845
|
+
if (inHead.code !== 0) {
|
|
10846
|
+
return splitLines(content).map((line, i) => ({
|
|
10847
|
+
sha: ZERO_SHA,
|
|
10848
|
+
origLine: i + 1,
|
|
10849
|
+
finalLine: i + 1,
|
|
10850
|
+
author: "Not Committed Yet",
|
|
10851
|
+
authorMail: "not.committed.yet",
|
|
10852
|
+
filename: rel,
|
|
10853
|
+
boundary: false,
|
|
10854
|
+
uncommitted: true,
|
|
10855
|
+
content: line
|
|
10856
|
+
}));
|
|
10857
|
+
}
|
|
10858
|
+
throw new BlameUnreadable(rel, out.stderr.trim());
|
|
10859
|
+
}
|
|
10860
|
+
return parseLinePorcelain(text(out));
|
|
10861
|
+
}
|
|
10862
|
+
async function commitEvidence(run2, root, entry, blame, history, blobs, manifest) {
|
|
10863
|
+
const pathAt = /* @__PURE__ */ new Map();
|
|
10864
|
+
for (const line of blame) {
|
|
10865
|
+
if (!line.uncommitted && !pathAt.has(line.sha)) pathAt.set(line.sha, unquotePath(line.filename));
|
|
10866
|
+
}
|
|
10867
|
+
const shas = [...pathAt.keys()];
|
|
10868
|
+
const commits = /* @__PURE__ */ new Map();
|
|
10869
|
+
if (shas.length === 0) return commits;
|
|
10870
|
+
const trailers = /* @__PURE__ */ new Map();
|
|
10871
|
+
const pageBlob = /* @__PURE__ */ new Map();
|
|
10872
|
+
for (const h of history) {
|
|
10873
|
+
trailers.set(h.sha, { generatedBy: h.generatedBy, coAuthoredBy: h.coAuthors });
|
|
10874
|
+
const blob = h.newBlob === null ? void 0 : blobs.get(h.newBlob);
|
|
10875
|
+
if (blob !== void 0) pageBlob.set(h.sha, blob);
|
|
10876
|
+
}
|
|
10877
|
+
const unlogged = shas.filter((sha) => !trailers.has(sha));
|
|
10878
|
+
if (unlogged.length > 0) {
|
|
10879
|
+
const out = await run2(["log", "--no-walk=unsorted", RECORD_FORMAT, ...unlogged]);
|
|
10880
|
+
if (out.tooLarge) throw new HistoryTooLarge();
|
|
10881
|
+
if (out.code !== 0) throw new BlobsUnreadable(`git log exit ${String(out.code)}`);
|
|
10882
|
+
for (const record of parseLog(text(out))) {
|
|
10883
|
+
trailers.set(record.sha, { generatedBy: record.generatedBy, coAuthoredBy: record.coAuthors });
|
|
10884
|
+
}
|
|
10885
|
+
}
|
|
10886
|
+
const manifestRel = manifest === void 0 ? void 0 : insideRoot(root, manifest.absPath);
|
|
10887
|
+
const specs = [];
|
|
10888
|
+
for (const sha of shas) {
|
|
10889
|
+
if (!pageBlob.has(sha)) specs.push(`${sha}:${pathAt.get(sha) ?? ""}`);
|
|
10890
|
+
if (manifestRel !== void 0) specs.push(`${sha}:${manifestRel}`);
|
|
10891
|
+
}
|
|
10892
|
+
const fetched = await fetchSpecs(run2, specs);
|
|
10893
|
+
for (const sha of shas) {
|
|
10894
|
+
const blob = pageBlob.get(sha) ?? fetched.get(`${sha}:${pathAt.get(sha) ?? ""}`);
|
|
10895
|
+
commits.set(sha, {
|
|
10896
|
+
sha,
|
|
10897
|
+
trailers: trailers.get(sha) ?? { generatedBy: [], coAuthoredBy: [] },
|
|
10898
|
+
...blob !== void 0 ? { blob } : {},
|
|
10899
|
+
...manifest !== void 0 ? {
|
|
10900
|
+
stamp: manifestRel === void 0 ? [] : manifestStamp(
|
|
10901
|
+
fetched.get(`${sha}:${manifestRel}`),
|
|
10902
|
+
manifest.join === "path" ? entryAt(manifest.entry, entry.rel, pathAt.get(sha) ?? entry.rel) : manifest.entry,
|
|
10903
|
+
manifest.join
|
|
10904
|
+
)
|
|
10905
|
+
} : {}
|
|
10906
|
+
});
|
|
10907
|
+
}
|
|
10908
|
+
return commits;
|
|
10909
|
+
}
|
|
10910
|
+
function insideRoot(root, abs) {
|
|
10911
|
+
const rel = relative10(root, abs);
|
|
10912
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute5(rel)) return void 0;
|
|
10913
|
+
return rel.split(sep5).join("/");
|
|
10914
|
+
}
|
|
10915
|
+
function entryAt(entry, rel, relAtCommit) {
|
|
10916
|
+
if (relAtCommit === rel) return entry;
|
|
10917
|
+
const fromHere = posix2.relative(posix2.dirname(rel), relAtCommit);
|
|
10918
|
+
return posix2.join(posix2.dirname(entry), fromHere);
|
|
10919
|
+
}
|
|
10920
|
+
function manifestStamp(text2, key, join10) {
|
|
10921
|
+
if (text2 === void 0) return [];
|
|
10922
|
+
let data;
|
|
10923
|
+
try {
|
|
10924
|
+
data = parseYaml4(text2);
|
|
10925
|
+
} catch {
|
|
10926
|
+
return [];
|
|
10927
|
+
}
|
|
10928
|
+
if (!isRecord4(data)) return [];
|
|
10929
|
+
for (const [candidate, value] of Object.entries(data)) {
|
|
10930
|
+
const same = join10 === "path" ? posix2.normalize(candidate) === posix2.normalize(key) : candidate === key;
|
|
10931
|
+
if (same && isRecord4(value)) return provenanceEntries(value[PROVENANCE_FIELD]);
|
|
10932
|
+
}
|
|
10933
|
+
return [];
|
|
10934
|
+
}
|
|
10935
|
+
function isRecord4(x) {
|
|
10936
|
+
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
10937
|
+
}
|
|
10938
|
+
async function fetchSpecs(run2, specs) {
|
|
10939
|
+
const found = /* @__PURE__ */ new Map();
|
|
10940
|
+
if (specs.length === 0) return found;
|
|
10941
|
+
const out = await run2(["cat-file", "--batch"], `${specs.join("\n")}
|
|
10942
|
+
`);
|
|
10943
|
+
if (out.tooLarge) throw new HistoryTooLarge();
|
|
10944
|
+
if (out.code !== 0) throw new BlobsUnreadable(`exit ${String(out.code)}`);
|
|
10945
|
+
const buf = out.raw;
|
|
10946
|
+
let cursor = 0;
|
|
10947
|
+
for (const spec of specs) {
|
|
10948
|
+
const nl = buf.indexOf(10, cursor);
|
|
10949
|
+
if (nl === -1) break;
|
|
10950
|
+
const header = buf.toString("utf8", cursor, nl);
|
|
10951
|
+
cursor = nl + 1;
|
|
10952
|
+
if (header.endsWith(" missing") || header.endsWith(" ambiguous")) continue;
|
|
10953
|
+
const parts = header.split(" ");
|
|
10954
|
+
const size = Number(parts[2]);
|
|
10955
|
+
if (!Number.isFinite(size)) break;
|
|
10956
|
+
if (parts[1] === "blob") found.set(spec, buf.toString("utf8", cursor, cursor + size));
|
|
10957
|
+
cursor += size + 1;
|
|
10958
|
+
}
|
|
10959
|
+
return found;
|
|
10960
|
+
}
|
|
10146
10961
|
function extractorFor(input) {
|
|
10147
10962
|
return extractorByName(input.extracted.format) ?? extractorForExtension(extname2(input.absPath));
|
|
10148
10963
|
}
|
|
@@ -10161,7 +10976,7 @@ function sameBody(a, b) {
|
|
|
10161
10976
|
function sameValue(a, b) {
|
|
10162
10977
|
return toJsonText(a) === toJsonText(b);
|
|
10163
10978
|
}
|
|
10164
|
-
var
|
|
10979
|
+
var short2 = (sha) => sha.slice(0, 7);
|
|
10165
10980
|
var datePart = (iso) => iso.slice(0, 10);
|
|
10166
10981
|
function localDate(d) {
|
|
10167
10982
|
const pad2 = (n) => String(n).padStart(2, "0");
|
|
@@ -10175,6 +10990,7 @@ function emailOf(trailer) {
|
|
|
10175
10990
|
const m = /<([^>]*)>\s*$/.exec(trailer);
|
|
10176
10991
|
return m?.[1]?.trim() ?? null;
|
|
10177
10992
|
}
|
|
10993
|
+
var MAX_STDERR_BYTES = 16 * 1024;
|
|
10178
10994
|
function text(out) {
|
|
10179
10995
|
try {
|
|
10180
10996
|
return out.raw.toString("utf8");
|
|
@@ -10190,7 +11006,7 @@ function runGit(args, cwd, stdin, maxBytes) {
|
|
|
10190
11006
|
done = true;
|
|
10191
11007
|
settle(out);
|
|
10192
11008
|
};
|
|
10193
|
-
const
|
|
11009
|
+
const fail2 = (err) => {
|
|
10194
11010
|
if (done) return;
|
|
10195
11011
|
done = true;
|
|
10196
11012
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
@@ -10199,7 +11015,8 @@ function runGit(args, cwd, stdin, maxBytes) {
|
|
|
10199
11015
|
code: null,
|
|
10200
11016
|
raw: Buffer.alloc(0),
|
|
10201
11017
|
missing,
|
|
10202
|
-
tooLarge: false
|
|
11018
|
+
tooLarge: false,
|
|
11019
|
+
stderr: ""
|
|
10203
11020
|
});
|
|
10204
11021
|
let child;
|
|
10205
11022
|
try {
|
|
@@ -10222,7 +11039,14 @@ function runGit(args, cwd, stdin, maxBytes) {
|
|
|
10222
11039
|
}
|
|
10223
11040
|
chunks.push(chunk);
|
|
10224
11041
|
});
|
|
10225
|
-
|
|
11042
|
+
const errChunks = [];
|
|
11043
|
+
let errTotal = 0;
|
|
11044
|
+
child.stderr.on("data", (chunk) => {
|
|
11045
|
+
if (errTotal >= MAX_STDERR_BYTES) return;
|
|
11046
|
+
const kept = chunk.subarray(0, MAX_STDERR_BYTES - errTotal);
|
|
11047
|
+
errTotal += kept.length;
|
|
11048
|
+
errChunks.push(kept);
|
|
11049
|
+
});
|
|
10226
11050
|
child.on("error", (err) => {
|
|
10227
11051
|
finish(failed(err.code === "ENOENT"));
|
|
10228
11052
|
});
|
|
@@ -10231,9 +11055,10 @@ function runGit(args, cwd, stdin, maxBytes) {
|
|
|
10231
11055
|
child.on("close", (code) => {
|
|
10232
11056
|
try {
|
|
10233
11057
|
const raw = tooLarge ? Buffer.alloc(0) : Buffer.concat(chunks);
|
|
10234
|
-
|
|
11058
|
+
const stderr = Buffer.concat(errChunks).toString("utf8");
|
|
11059
|
+
finish({ code, raw, missing: false, tooLarge, stderr });
|
|
10235
11060
|
} catch (err) {
|
|
10236
|
-
|
|
11061
|
+
fail2(err);
|
|
10237
11062
|
}
|
|
10238
11063
|
});
|
|
10239
11064
|
if (stdin === void 0) child.stdin.end();
|
|
@@ -10248,7 +11073,8 @@ var FIELD_SOURCES = {
|
|
|
10248
11073
|
authors: ["git"],
|
|
10249
11074
|
owner: ["codeowners"],
|
|
10250
11075
|
"reviewed-by": ["github", "gitlab", "git"],
|
|
10251
|
-
"last-reviewed": ["github", "gitlab", "git"]
|
|
11076
|
+
"last-reviewed": ["github", "gitlab", "git"],
|
|
11077
|
+
provenance: ["git"]
|
|
10252
11078
|
};
|
|
10253
11079
|
var REVIEW_SOURCES = ["github", "gitlab"];
|
|
10254
11080
|
var HOST_NAME = {
|
|
@@ -10282,10 +11108,20 @@ async function deriveMetadata(inputs, ctx) {
|
|
|
10282
11108
|
const sources = {};
|
|
10283
11109
|
let git2 = /* @__PURE__ */ new Map();
|
|
10284
11110
|
if (consulted.has("git")) {
|
|
10285
|
-
const result = await deriveFromGit(inputs, {
|
|
11111
|
+
const result = await deriveFromGit(inputs, {
|
|
11112
|
+
cwd: ctx.cwd,
|
|
11113
|
+
now: ctx.now,
|
|
11114
|
+
fields: ctx.fields,
|
|
11115
|
+
...ctx.generatedBy !== void 0 ? { generatedBy: ctx.generatedBy } : {},
|
|
11116
|
+
...ctx.machines !== void 0 ? { machines: ctx.machines } : {}
|
|
11117
|
+
});
|
|
10286
11118
|
sources.git = result.status;
|
|
10287
11119
|
git2 = result.records;
|
|
10288
11120
|
}
|
|
11121
|
+
const provenance = /* @__PURE__ */ new Map();
|
|
11122
|
+
for (const [label, facts] of git2) {
|
|
11123
|
+
if (facts.provenanceDerivation !== void 0) provenance.set(label, facts.provenanceDerivation);
|
|
11124
|
+
}
|
|
10289
11125
|
const rootCache = /* @__PURE__ */ new Map();
|
|
10290
11126
|
const rootOf = (absPath) => {
|
|
10291
11127
|
const dir = dirname9(absPath);
|
|
@@ -10363,7 +11199,7 @@ async function deriveMetadata(inputs, ctx) {
|
|
|
10363
11199
|
}
|
|
10364
11200
|
records.set(input.label, { file: input.label, fields });
|
|
10365
11201
|
}
|
|
10366
|
-
return { records, sources };
|
|
11202
|
+
return { records, sources, ...provenance.size > 0 ? { provenance } : {} };
|
|
10367
11203
|
}
|
|
10368
11204
|
function valueFrom(source, field, git2, reviews, owner, commanded) {
|
|
10369
11205
|
switch (source) {
|
|
@@ -10446,6 +11282,9 @@ function commandsOf(config) {
|
|
|
10446
11282
|
}
|
|
10447
11283
|
return out;
|
|
10448
11284
|
}
|
|
11285
|
+
function machinesOf(config) {
|
|
11286
|
+
return config?.machines ?? DEFAULT_MACHINES;
|
|
11287
|
+
}
|
|
10449
11288
|
|
|
10450
11289
|
// src/meta/core/derive/table.ts
|
|
10451
11290
|
var DERIVED_VIEW = "derived";
|
|
@@ -10460,10 +11299,11 @@ function mentionsDerived(sql) {
|
|
|
10460
11299
|
return /\b(?:from|join|update|into|table|view)\s+(?:"derived"|derived\b)/i.test(sql);
|
|
10461
11300
|
}
|
|
10462
11301
|
function fieldsForSql(sql, fields) {
|
|
10463
|
-
|
|
10464
|
-
|
|
10465
|
-
(field) =>
|
|
10466
|
-
|
|
11302
|
+
const named = (field) => new RegExp(`(?<![\\w-])${escapeRegExp(field)}(?![\\w-])`, "i").test(sql);
|
|
11303
|
+
if (sql.includes("*") || /\b_(?:sources|origin)\b/i.test(sql)) {
|
|
11304
|
+
return fields.filter((field) => field !== PROVENANCE_FIELD || named(field));
|
|
11305
|
+
}
|
|
11306
|
+
return fields.filter(named);
|
|
10467
11307
|
}
|
|
10468
11308
|
function escapeRegExp(s) {
|
|
10469
11309
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -10557,16 +11397,18 @@ function createResolvedView(db, docsColumns, commands) {
|
|
|
10557
11397
|
async function deriveForTable(inputs, ctx, hint, fields) {
|
|
10558
11398
|
const derive = ctx.config?.derive;
|
|
10559
11399
|
const commands = commandsOf(derive);
|
|
11400
|
+
const managesProvenance = derive?.fields.includes(PROVENANCE_FIELD) === true && !fields.includes(PROVENANCE_FIELD);
|
|
10560
11401
|
const result = await deriveMetadata(inputs, {
|
|
10561
11402
|
cwd: ctx.cwd,
|
|
10562
11403
|
base: ctx.base,
|
|
10563
11404
|
...ctx.configDir !== void 0 ? { configDir: ctx.configDir } : {},
|
|
10564
11405
|
sources: derive?.sources ?? [...DERIVE_SOURCES],
|
|
10565
|
-
fields,
|
|
11406
|
+
fields: managesProvenance ? [...fields, PROVENANCE_FIELD] : fields,
|
|
10566
11407
|
...derive?.codeowners !== void 0 ? { codeowners: derive.codeowners } : {},
|
|
10567
11408
|
...commands !== void 0 ? { commands } : {},
|
|
10568
11409
|
cache: ctx.cache,
|
|
10569
|
-
now: ctx.now ?? (() => /* @__PURE__ */ new Date())
|
|
11410
|
+
now: ctx.now ?? (() => /* @__PURE__ */ new Date()),
|
|
11411
|
+
machines: machinesOf(derive)
|
|
10570
11412
|
});
|
|
10571
11413
|
assertSourcesAvailable(result.sources, hint);
|
|
10572
11414
|
return result.records;
|
|
@@ -10574,7 +11416,7 @@ async function deriveForTable(inputs, ctx, hint, fields) {
|
|
|
10574
11416
|
|
|
10575
11417
|
// src/meta/commands/validate.ts
|
|
10576
11418
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
10577
|
-
import { resolve as
|
|
11419
|
+
import { resolve as resolve13, extname as extname3 } from "node:path";
|
|
10578
11420
|
|
|
10579
11421
|
// src/meta/core/checks.ts
|
|
10580
11422
|
var CHECK_KEYWORD = "check";
|
|
@@ -10711,6 +11553,71 @@ async function runChecks(checks, entries, ctx = {}) {
|
|
|
10711
11553
|
return merged;
|
|
10712
11554
|
}
|
|
10713
11555
|
|
|
11556
|
+
// src/meta/core/derive/provenance-place.ts
|
|
11557
|
+
import { isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep6 } from "node:path";
|
|
11558
|
+
var toPosix3 = (p) => p.split(sep6).join("/");
|
|
11559
|
+
function provenanceManifests(collections, configDir, base) {
|
|
11560
|
+
const out = [];
|
|
11561
|
+
for (const collection of collections) {
|
|
11562
|
+
for (const manifest of collection.externalMetadata) {
|
|
11563
|
+
if (!manifest.keys.includes(PROVENANCE_FIELD)) continue;
|
|
11564
|
+
if (classifyRef(manifest.file).kind === "url") {
|
|
11565
|
+
throw new DocmetaError(
|
|
11566
|
+
`collection ${collection.name}: ${PROVENANCE_FIELD} cannot come from a URL manifest, because manni meta derive writes it.`
|
|
11567
|
+
);
|
|
11568
|
+
}
|
|
11569
|
+
const absPath = isAbsolute6(manifest.file) ? manifest.file : resolve12(configDir, manifest.file);
|
|
11570
|
+
const rel = relative11(base, absPath);
|
|
11571
|
+
out.push({
|
|
11572
|
+
collection: collection.name,
|
|
11573
|
+
absPath,
|
|
11574
|
+
file: rel === "" ? "." : toPosix3(rel),
|
|
11575
|
+
join: externalMetadataJoin(manifest)
|
|
11576
|
+
});
|
|
11577
|
+
}
|
|
11578
|
+
}
|
|
11579
|
+
return out;
|
|
11580
|
+
}
|
|
11581
|
+
function provenancePlace(label, data, manifests, collections, configDir, base) {
|
|
11582
|
+
if (manifests.length === 0) return void 0;
|
|
11583
|
+
const members = new Set(memberOf(collections, configDir, base, label));
|
|
11584
|
+
const owning = manifests.filter((m) => members.has(m.collection));
|
|
11585
|
+
const [manifest, second] = owning;
|
|
11586
|
+
if (manifest === void 0) return void 0;
|
|
11587
|
+
if (second !== void 0) {
|
|
11588
|
+
throw new DocmetaError(
|
|
11589
|
+
`${label} is in collections ${manifest.collection} and ${second.collection}, and both keep ${PROVENANCE_FIELD} in a manifest.`
|
|
11590
|
+
);
|
|
11591
|
+
}
|
|
11592
|
+
if (manifest.join === PATH_JOIN) {
|
|
11593
|
+
const entry = toPosix3(relative11(configDir, resolve12(base, label)));
|
|
11594
|
+
return { manifest, absPath: manifest.absPath, entry, join: manifest.join };
|
|
11595
|
+
}
|
|
11596
|
+
const value = data[manifest.join];
|
|
11597
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
11598
|
+
throw new DocmetaError(
|
|
11599
|
+
`${label} carries no ${manifest.join}, which ${manifest.file} joins on, so its ${PROVENANCE_FIELD} has no entry there.`
|
|
11600
|
+
);
|
|
11601
|
+
}
|
|
11602
|
+
return { manifest, absPath: manifest.absPath, entry: String(value), join: manifest.join };
|
|
11603
|
+
}
|
|
11604
|
+
function readerManifests(collections, configDir, base) {
|
|
11605
|
+
const local = collections.map((c) => ({
|
|
11606
|
+
...c,
|
|
11607
|
+
externalMetadata: c.externalMetadata.filter((m) => classifyRef(m.file).kind !== "url")
|
|
11608
|
+
}));
|
|
11609
|
+
return provenanceManifests(local, configDir, base);
|
|
11610
|
+
}
|
|
11611
|
+
function readerPlace(label, data, manifests, collections, configDir, base) {
|
|
11612
|
+
try {
|
|
11613
|
+
const place = provenancePlace(label, data, manifests, collections, configDir, base);
|
|
11614
|
+
return place === void 0 ? void 0 : { absPath: place.absPath, entry: place.entry, join: place.join };
|
|
11615
|
+
} catch (err) {
|
|
11616
|
+
if (err instanceof DocmetaError) return void 0;
|
|
11617
|
+
throw err;
|
|
11618
|
+
}
|
|
11619
|
+
}
|
|
11620
|
+
|
|
10714
11621
|
// src/meta/commands/validate.ts
|
|
10715
11622
|
function parseErrorResult(file, format, message, keyword) {
|
|
10716
11623
|
const err = {
|
|
@@ -10834,6 +11741,7 @@ async function runValidate(opts) {
|
|
|
10834
11741
|
const deriveFields = new Set(deriveWillRun ? deriveConfig.fields : []);
|
|
10835
11742
|
const readable = derivableFields(deriveCommands);
|
|
10836
11743
|
for (const c of derivedChecks) for (const f of fieldsForSql(c.query, readable)) deriveFields.add(f);
|
|
11744
|
+
const provenanceManifests2 = keepDeriveInputs && deriveFields.has(PROVENANCE_FIELD) ? readerManifests(declaredCollections, configDir ?? cwd, base) : [];
|
|
10837
11745
|
const derivedOnce = once(async () => {
|
|
10838
11746
|
const derived = await deriveMetadata(deriveInputs, {
|
|
10839
11747
|
cwd,
|
|
@@ -10844,7 +11752,8 @@ async function runValidate(opts) {
|
|
|
10844
11752
|
...deriveConfig?.codeowners !== void 0 ? { codeowners: deriveConfig.codeowners } : {},
|
|
10845
11753
|
...deriveCommands !== void 0 ? { commands: deriveCommands } : {},
|
|
10846
11754
|
cache: opts.cache ?? true,
|
|
10847
|
-
now: opts.now ?? (() => /* @__PURE__ */ new Date())
|
|
11755
|
+
now: opts.now ?? (() => /* @__PURE__ */ new Date()),
|
|
11756
|
+
machines: machinesOf(deriveConfig)
|
|
10848
11757
|
});
|
|
10849
11758
|
assertSourcesAvailable(
|
|
10850
11759
|
derived.sources,
|
|
@@ -10878,7 +11787,14 @@ async function runValidate(opts) {
|
|
|
10878
11787
|
return;
|
|
10879
11788
|
}
|
|
10880
11789
|
if (keepDeriveInputs && label !== STDIN_LABEL) {
|
|
10881
|
-
|
|
11790
|
+
const place = provenanceManifests2.length === 0 ? void 0 : readerPlace(label, extracted.data, provenanceManifests2, declaredCollections, configDir ?? cwd, base);
|
|
11791
|
+
deriveInputs.push({
|
|
11792
|
+
label,
|
|
11793
|
+
absPath: resolve13(base, label),
|
|
11794
|
+
content,
|
|
11795
|
+
extracted,
|
|
11796
|
+
...place !== void 0 ? { provenanceManifest: place } : {}
|
|
11797
|
+
});
|
|
10882
11798
|
}
|
|
10883
11799
|
const merged = mergeExternalMetadata(label, extracted, externalMetadata, members, base, {
|
|
10884
11800
|
encryptionKey
|
|
@@ -10991,7 +11907,7 @@ async function runValidate(opts) {
|
|
|
10991
11907
|
await processOne(STDIN_LABEL, content, forcedExtractor.extensions[0] ?? "");
|
|
10992
11908
|
}
|
|
10993
11909
|
for (const file of files) {
|
|
10994
|
-
const content = await readFile8(
|
|
11910
|
+
const content = await readFile8(resolve13(base, file), "utf8");
|
|
10995
11911
|
await processOne(file, content, extname3(file));
|
|
10996
11912
|
}
|
|
10997
11913
|
if (unverified > 0) opts.onNotice?.(unverifiedWarning(unverified));
|
|
@@ -11081,18 +11997,23 @@ async function runValidate(opts) {
|
|
|
11081
11997
|
}
|
|
11082
11998
|
marked = [...managed.marked, ...await validator.markedPointers(stamped, managed.refs)];
|
|
11083
11999
|
}
|
|
11084
|
-
const
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
12000
|
+
const asserted = (field) => managed !== void 0 ? managed.data[field] : input.extracted.data[field];
|
|
12001
|
+
const findings = fields.flatMap((field) => {
|
|
12002
|
+
if (field === PROVENANCE_FIELD) {
|
|
12003
|
+
const derivation = derived.provenance?.get(input.label);
|
|
12004
|
+
if (derivation === void 0) return [];
|
|
12005
|
+
return provenanceFindings(
|
|
12006
|
+
compareProvenance(provenanceEntries(asserted(field)), derivation),
|
|
12007
|
+
derivation.page.bodyLine
|
|
11090
12008
|
);
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
12009
|
+
}
|
|
12010
|
+
const compared = compareDerived(field, asserted(field), record.fields[field]);
|
|
12011
|
+
const at2 = pointerOf(field);
|
|
12012
|
+
return staleFindings(
|
|
12013
|
+
[marked.some((p) => isAtOrUnder(p, at2)) ? redactedDerived(compared) : compared],
|
|
12014
|
+
input.extracted.lineFor
|
|
12015
|
+
);
|
|
12016
|
+
});
|
|
11096
12017
|
if (findings.length === 0) continue;
|
|
11097
12018
|
result.errors.push(...findings);
|
|
11098
12019
|
result.ok = false;
|
|
@@ -11130,13 +12051,13 @@ async function runValidate(opts) {
|
|
|
11130
12051
|
|
|
11131
12052
|
// src/meta/commands/get.ts
|
|
11132
12053
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
11133
|
-
import { resolve as
|
|
12054
|
+
import { resolve as resolve14, extname as extname4 } from "node:path";
|
|
11134
12055
|
async function runGet(opts) {
|
|
11135
12056
|
const cwd = opts.cwd ?? process.cwd();
|
|
11136
12057
|
if (opts.fields.length === 0) {
|
|
11137
12058
|
throw new DocmetaError("Specify at least one field to get.");
|
|
11138
12059
|
}
|
|
11139
|
-
const { config, inputs, base, configDir, collections, fromCollections, configFile } = await resolveRunConfig({
|
|
12060
|
+
const { config, inputs, base, configDir, collections, declaredCollections, fromCollections, configFile } = await resolveRunConfig({
|
|
11140
12061
|
cwd,
|
|
11141
12062
|
configPath: opts.configPath,
|
|
11142
12063
|
noConfig: opts.noConfig,
|
|
@@ -11195,7 +12116,10 @@ async function runGet(opts) {
|
|
|
11195
12116
|
const deriving = opts.derived !== false;
|
|
11196
12117
|
const derivableFields2 = deriving ? opts.fields.filter((f) => isBuiltinField(f) || Object.hasOwn(commands, f)) : [];
|
|
11197
12118
|
const deriveInputs = [];
|
|
12119
|
+
const wantsProvenance = opts.fields.includes(PROVENANCE_FIELD);
|
|
12120
|
+
const provenanceManifests2 = deriving && derivableFields2.includes(PROVENANCE_FIELD) ? readerManifests(declaredCollections, configDir ?? cwd, base) : [];
|
|
11198
12121
|
const carried = /* @__PURE__ */ new Map();
|
|
12122
|
+
const bodyLines = /* @__PURE__ */ new Map();
|
|
11199
12123
|
const readOne = (label, content, extension) => {
|
|
11200
12124
|
const extractor = forced ?? extractorForExtension(extension);
|
|
11201
12125
|
if (!extractor) {
|
|
@@ -11210,13 +12134,18 @@ async function runGet(opts) {
|
|
|
11210
12134
|
elements: resolveElements(label, config, members)
|
|
11211
12135
|
});
|
|
11212
12136
|
if (deriving && label !== STDIN_LABEL) {
|
|
12137
|
+
const place = provenanceManifests2.length === 0 ? void 0 : readerPlace(label, own2.data, provenanceManifests2, declaredCollections, configDir ?? cwd, base);
|
|
11213
12138
|
deriveInputs.push({
|
|
11214
12139
|
label,
|
|
11215
|
-
absPath:
|
|
12140
|
+
absPath: resolve14(base, label),
|
|
11216
12141
|
content,
|
|
11217
|
-
extracted: own2
|
|
12142
|
+
extracted: own2,
|
|
12143
|
+
...place !== void 0 ? { provenanceManifest: place } : {}
|
|
11218
12144
|
});
|
|
11219
12145
|
}
|
|
12146
|
+
if (wantsProvenance) {
|
|
12147
|
+
bodyLines.set(label, readProvenancePage(content, { fenced: provenanceFenced(own2) }).bodyLine);
|
|
12148
|
+
}
|
|
11220
12149
|
extracted = mergeExternalMetadata(label, own2, externalMetadata, members, base, {
|
|
11221
12150
|
encryptionKey: joinKey
|
|
11222
12151
|
}).extracted;
|
|
@@ -11233,7 +12162,13 @@ async function runGet(opts) {
|
|
|
11233
12162
|
carries[f] = carriesField(extracted.data, f);
|
|
11234
12163
|
}
|
|
11235
12164
|
carried.set(label, carries);
|
|
11236
|
-
|
|
12165
|
+
const bodyLine = bodyLines.get(label);
|
|
12166
|
+
out.push({
|
|
12167
|
+
file: label,
|
|
12168
|
+
present: extracted.present,
|
|
12169
|
+
values,
|
|
12170
|
+
...bodyLine !== void 0 ? { provenance: { bodyLine } } : {}
|
|
12171
|
+
});
|
|
11237
12172
|
};
|
|
11238
12173
|
if (usingStdin) {
|
|
11239
12174
|
if (!forced) {
|
|
@@ -11244,7 +12179,7 @@ async function runGet(opts) {
|
|
|
11244
12179
|
readOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
|
|
11245
12180
|
}
|
|
11246
12181
|
for (const file of files) {
|
|
11247
|
-
const content = await readFile9(
|
|
12182
|
+
const content = await readFile9(resolve14(base, file), "utf8");
|
|
11248
12183
|
readOne(file, content, extname4(file));
|
|
11249
12184
|
}
|
|
11250
12185
|
if (deriving) {
|
|
@@ -11267,6 +12202,7 @@ async function runGet(opts) {
|
|
|
11267
12202
|
async function attachResolved(out, inputs, opts) {
|
|
11268
12203
|
const { run: run2 } = opts;
|
|
11269
12204
|
const derive = run2.config?.derive;
|
|
12205
|
+
let provenance = /* @__PURE__ */ new Map();
|
|
11270
12206
|
const commands = commandsOf(derive);
|
|
11271
12207
|
const records = opts.derivable.length === 0 || inputs.length === 0 ? /* @__PURE__ */ new Map() : await (async () => {
|
|
11272
12208
|
const result = await deriveMetadata(inputs, {
|
|
@@ -11278,12 +12214,14 @@ async function attachResolved(out, inputs, opts) {
|
|
|
11278
12214
|
...derive?.codeowners !== void 0 ? { codeowners: derive.codeowners } : {},
|
|
11279
12215
|
...commands !== void 0 ? { commands } : {},
|
|
11280
12216
|
cache: run2.cache,
|
|
11281
|
-
now: run2.now
|
|
12217
|
+
now: run2.now,
|
|
12218
|
+
machines: machinesOf(derive)
|
|
11282
12219
|
});
|
|
11283
12220
|
assertSourcesAvailable(
|
|
11284
12221
|
result.sources,
|
|
11285
12222
|
"pass --no-derived, or narrow derive.sources"
|
|
11286
12223
|
);
|
|
12224
|
+
if (result.provenance !== void 0) provenance = result.provenance;
|
|
11287
12225
|
return result.records;
|
|
11288
12226
|
})();
|
|
11289
12227
|
for (const r of out) {
|
|
@@ -11311,6 +12249,11 @@ async function attachResolved(out, inputs, opts) {
|
|
|
11311
12249
|
if (opts.derivable.length > 0) r.derived = derived;
|
|
11312
12250
|
r.resolved = resolved;
|
|
11313
12251
|
r.origin = origin;
|
|
12252
|
+
const derivation = provenance.get(r.file);
|
|
12253
|
+
if (r.provenance !== void 0 && derivation !== void 0) {
|
|
12254
|
+
const comparisons = compareProvenance(provenanceEntries(r.values[PROVENANCE_FIELD]), derivation);
|
|
12255
|
+
r.provenance.current = comparisons.every((c) => c.status === "current");
|
|
12256
|
+
}
|
|
11314
12257
|
}
|
|
11315
12258
|
}
|
|
11316
12259
|
function resolveField(data, field) {
|
|
@@ -11363,15 +12306,15 @@ function parseJsonPointer(pointer) {
|
|
|
11363
12306
|
// src/meta/commands/schemas.ts
|
|
11364
12307
|
import { existsSync as existsSync4 } from "node:fs";
|
|
11365
12308
|
import { mkdir as mkdir2, readFile as readFile10 } from "node:fs/promises";
|
|
11366
|
-
import { dirname as dirname10, extname as extname5, isAbsolute as
|
|
11367
|
-
import { parseDocument as parseDocument5, parse as
|
|
12309
|
+
import { dirname as dirname10, extname as extname5, isAbsolute as isAbsolute7, join as join8, relative as relative12, resolve as resolve15, sep as sep7 } from "node:path";
|
|
12310
|
+
import { parseDocument as parseDocument5, parse as parseYaml5, stringify as stringify2 } from "yaml";
|
|
11368
12311
|
function getSchemasInfo() {
|
|
11369
12312
|
return { builtins: listBuiltins(), formats: listFormats() };
|
|
11370
12313
|
}
|
|
11371
12314
|
var DEFAULT_VENDOR_DIR = "./schema";
|
|
11372
12315
|
var DEFAULT_CONFIG_NAME = "manni.config.yaml";
|
|
11373
12316
|
function posixRelative(from, to) {
|
|
11374
|
-
return
|
|
12317
|
+
return relative12(from, to).split(sep7).join("/");
|
|
11375
12318
|
}
|
|
11376
12319
|
function vendorFileName(url) {
|
|
11377
12320
|
let parsed2;
|
|
@@ -11445,7 +12388,7 @@ function reparseConfig(text2, source, section) {
|
|
|
11445
12388
|
parseConfig(text2, source);
|
|
11446
12389
|
return;
|
|
11447
12390
|
}
|
|
11448
|
-
const doc =
|
|
12391
|
+
const doc = parseYaml5(text2);
|
|
11449
12392
|
parseConfigValue(doc[section], source, section);
|
|
11450
12393
|
}
|
|
11451
12394
|
async function runVendorSchema(opts) {
|
|
@@ -11457,13 +12400,13 @@ async function runVendorSchema(opts) {
|
|
|
11457
12400
|
);
|
|
11458
12401
|
}
|
|
11459
12402
|
const loaded = await loadConfig(opts.configPath, cwd);
|
|
11460
|
-
const configPath = loaded?.path ??
|
|
12403
|
+
const configPath = loaded?.path ?? resolve15(cwd, DEFAULT_CONFIG_NAME);
|
|
11461
12404
|
const configDir = loaded?.dir ?? cwd;
|
|
11462
|
-
const absDir =
|
|
12405
|
+
const absDir = resolve15(cwd, opts.dir ?? DEFAULT_VENDOR_DIR);
|
|
11463
12406
|
const absFile = join8(absDir, vendorFileName(url));
|
|
11464
12407
|
await assertNotIgnored(absFile, absDir, cwd, opts.onNotice);
|
|
11465
12408
|
const fromConfig = posixRelative(configDir, absFile);
|
|
11466
|
-
const ref =
|
|
12409
|
+
const ref = isAbsolute7(fromConfig) || fromConfig === "" ? absFile : fromConfig.startsWith(".") ? fromConfig : `./${fromConfig}`;
|
|
11467
12410
|
const { bytes } = await fetchSchemaBytes(url, {
|
|
11468
12411
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
11469
12412
|
...opts.maxBytes !== void 0 ? { maxBytes: opts.maxBytes } : {}
|
|
@@ -11640,7 +12583,7 @@ async function runInferSchema(opts) {
|
|
|
11640
12583
|
}
|
|
11641
12584
|
let absOut;
|
|
11642
12585
|
if (opts.out !== void 0) {
|
|
11643
|
-
absOut =
|
|
12586
|
+
absOut = resolve15(cwd, opts.out);
|
|
11644
12587
|
await assertNotIgnored(
|
|
11645
12588
|
absOut,
|
|
11646
12589
|
dirname10(absOut),
|
|
@@ -11728,7 +12671,7 @@ async function runInferSchema(opts) {
|
|
|
11728
12671
|
scanOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
|
|
11729
12672
|
}
|
|
11730
12673
|
for (const file of files) {
|
|
11731
|
-
const content = await readFile10(
|
|
12674
|
+
const content = await readFile10(resolve15(base, file), "utf8");
|
|
11732
12675
|
scanOne(file, content, extname5(file));
|
|
11733
12676
|
}
|
|
11734
12677
|
for (const u of unreadable) {
|
|
@@ -11790,7 +12733,7 @@ async function runInferSchema(opts) {
|
|
|
11790
12733
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
11791
12734
|
import { existsSync as existsSync5 } from "node:fs";
|
|
11792
12735
|
import { mkdir as mkdir3, open as open2, readFile as readFile11, rename as rename3, rm as rm3 } from "node:fs/promises";
|
|
11793
|
-
import { dirname as dirname11, isAbsolute as
|
|
12736
|
+
import { dirname as dirname11, isAbsolute as isAbsolute8, resolve as resolve16, extname as extname6, sep as sep8 } from "node:path";
|
|
11794
12737
|
|
|
11795
12738
|
// src/shared/prompt.ts
|
|
11796
12739
|
import { createInterface } from "node:readline";
|
|
@@ -12007,10 +12950,10 @@ async function runQuery(opts) {
|
|
|
12007
12950
|
readOne(STDIN_LABEL, opts.stdinContent ?? "", ext);
|
|
12008
12951
|
}
|
|
12009
12952
|
for (const file of files) {
|
|
12010
|
-
const content = await readFile11(
|
|
12953
|
+
const content = await readFile11(resolve16(base, file), "utf8");
|
|
12011
12954
|
readOne(file, content, extname6(file));
|
|
12012
12955
|
}
|
|
12013
|
-
const db = opts.db === void 0 ? void 0 : { resolved:
|
|
12956
|
+
const db = opts.db === void 0 ? void 0 : { resolved: resolve16(cwd, opts.db), display: opts.db };
|
|
12014
12957
|
const run2 = await runSql(sql, entries, {
|
|
12015
12958
|
target: db,
|
|
12016
12959
|
params: boundParams,
|
|
@@ -12141,13 +13084,21 @@ async function runSql(sql, entries, ctx) {
|
|
|
12141
13084
|
if (target) buildViews();
|
|
12142
13085
|
const buildDerived = async () => {
|
|
12143
13086
|
if (derivedTable.built) return;
|
|
12144
|
-
const inputs = entries.filter((e) => e.label !== STDIN_LABEL).map((e) => ({
|
|
12145
|
-
label: e.label,
|
|
12146
|
-
absPath: resolve15(ctx.base, e.label),
|
|
12147
|
-
content: e.content,
|
|
12148
|
-
extracted: e.own
|
|
12149
|
-
}));
|
|
12150
13087
|
const commands = commandsOf(ctx.config?.derive);
|
|
13088
|
+
const fields = fieldsForSql(sql, derivableFields(commands));
|
|
13089
|
+
const provenanceDerived = fields.includes(PROVENANCE_FIELD) || ctx.config?.derive?.fields.includes(PROVENANCE_FIELD) === true;
|
|
13090
|
+
const manifestRoot = ctx.configDir ?? ctx.cwd;
|
|
13091
|
+
const manifests = provenanceDerived ? readerManifests(ctx.declaredCollections, manifestRoot, ctx.base) : [];
|
|
13092
|
+
const inputs = entries.filter((e) => e.label !== STDIN_LABEL).map((e) => {
|
|
13093
|
+
const place = manifests.length === 0 ? void 0 : readerPlace(e.label, e.own.data, manifests, ctx.declaredCollections, manifestRoot, ctx.base);
|
|
13094
|
+
return {
|
|
13095
|
+
label: e.label,
|
|
13096
|
+
absPath: resolve16(ctx.base, e.label),
|
|
13097
|
+
content: e.content,
|
|
13098
|
+
extracted: e.own,
|
|
13099
|
+
...place !== void 0 ? { provenanceManifest: place } : {}
|
|
13100
|
+
};
|
|
13101
|
+
});
|
|
12151
13102
|
const records = await deriveForTable(
|
|
12152
13103
|
inputs,
|
|
12153
13104
|
{
|
|
@@ -12159,7 +13110,7 @@ async function runSql(sql, entries, ctx) {
|
|
|
12159
13110
|
...ctx.now !== void 0 ? { now: ctx.now } : {}
|
|
12160
13111
|
},
|
|
12161
13112
|
"narrow derive.sources in manni.config.yaml",
|
|
12162
|
-
|
|
13113
|
+
fields
|
|
12163
13114
|
);
|
|
12164
13115
|
createDerivedView(db, records, derivedColumns(commands));
|
|
12165
13116
|
createResolvedView(db, dataColumns, commands);
|
|
@@ -12601,20 +13552,20 @@ function displayPath(ctx, abs) {
|
|
|
12601
13552
|
}
|
|
12602
13553
|
function assertSchemaWriteWithin(ctx, abs) {
|
|
12603
13554
|
const within = posixRelative(ctx.trustRoot.dir, abs);
|
|
12604
|
-
if (within !== "" && !within.startsWith("..") && !
|
|
13555
|
+
if (within !== "" && !within.startsWith("..") && !isAbsolute8(within)) return;
|
|
12605
13556
|
throw new DocmetaError(
|
|
12606
13557
|
`The resolved schema "${abs}" lives outside ${ctx.trustRoot.dir}; DDL edits schemas inside the repository. Evolve it where it lives, or vendor a copy in.`
|
|
12607
13558
|
);
|
|
12608
13559
|
}
|
|
12609
13560
|
function foldPath(p) {
|
|
12610
|
-
const abs =
|
|
13561
|
+
const abs = resolve16(p);
|
|
12611
13562
|
return process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
12612
13563
|
}
|
|
12613
13564
|
function filePinsByPath(ctx) {
|
|
12614
13565
|
const byPath = /* @__PURE__ */ new Map();
|
|
12615
13566
|
for (const [ref, pin] of collectSchemaPins(ctx.config)) {
|
|
12616
13567
|
if (classifyRef(ref).kind !== "file") continue;
|
|
12617
|
-
byPath.set(foldPath(
|
|
13568
|
+
byPath.set(foldPath(resolve16(ctx.cwd, ref)), pin);
|
|
12618
13569
|
}
|
|
12619
13570
|
return byPath;
|
|
12620
13571
|
}
|
|
@@ -12642,7 +13593,7 @@ async function loadSetMembers(refs, ctx) {
|
|
|
12642
13593
|
});
|
|
12643
13594
|
continue;
|
|
12644
13595
|
}
|
|
12645
|
-
const abs =
|
|
13596
|
+
const abs = resolve16(ctx.cwd, ref);
|
|
12646
13597
|
const canon = foldPath(abs);
|
|
12647
13598
|
if (seenFiles.has(canon)) continue;
|
|
12648
13599
|
seenFiles.add(canon);
|
|
@@ -12847,7 +13798,7 @@ async function planSchemaMutation(ops, entries, ctx) {
|
|
|
12847
13798
|
const [, name2, ver] = target.builtinId.split(":");
|
|
12848
13799
|
const safe = (s) => s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
12849
13800
|
const forkName = `${safe(name2 ?? "schema")}-${safe(ver ?? "0")}.local.json`;
|
|
12850
|
-
schemaAbs =
|
|
13801
|
+
schemaAbs = resolve16(ctx.configDir ?? ctx.cwd, "schemas", forkName);
|
|
12851
13802
|
assertSchemaWriteWithin(ctx, schemaAbs);
|
|
12852
13803
|
if (existsSync5(schemaAbs)) {
|
|
12853
13804
|
throw new DocmetaError(
|
|
@@ -13014,7 +13965,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
|
|
|
13014
13965
|
}
|
|
13015
13966
|
return void 0;
|
|
13016
13967
|
};
|
|
13017
|
-
const rebaseRaw = (raw) => classifyRef(raw).kind === "file" && !
|
|
13968
|
+
const rebaseRaw = (raw) => classifyRef(raw).kind === "file" && !isAbsolute8(raw) ? resolve16(configDir, raw) : raw;
|
|
13018
13969
|
const wantSet = runRefs.map(rebaseRaw).sort().join("\n");
|
|
13019
13970
|
const seqResolvesToRunSet = (node) => {
|
|
13020
13971
|
if (!isSeq3(node)) return false;
|
|
@@ -13059,7 +14010,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
|
|
|
13059
14010
|
if (!isMap5(item)) continue;
|
|
13060
14011
|
const raw = rawRefOf(item);
|
|
13061
14012
|
if (raw === void 0 || classifyRef(raw).kind !== "file") continue;
|
|
13062
|
-
const abs =
|
|
14013
|
+
const abs = isAbsolute8(raw) ? raw : resolve16(configDir, raw);
|
|
13063
14014
|
if (foldPath(abs) !== wantPath) continue;
|
|
13064
14015
|
pinFrom = item.get("integrity");
|
|
13065
14016
|
item.set("integrity", edit.integrity.value);
|
|
@@ -13280,7 +14231,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
|
|
|
13280
14231
|
`"${file}": no writable format for that extension.`
|
|
13281
14232
|
);
|
|
13282
14233
|
}
|
|
13283
|
-
if (existsSync5(
|
|
14234
|
+
if (existsSync5(resolve16(ctx.base, file))) {
|
|
13284
14235
|
throw new DocmetaError(
|
|
13285
14236
|
`"${file}" already exists; INSERT creates new files only.`
|
|
13286
14237
|
);
|
|
@@ -13299,7 +14250,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
|
|
|
13299
14250
|
`"${from}" -> "${to}": a rename may not change the extension.`
|
|
13300
14251
|
);
|
|
13301
14252
|
}
|
|
13302
|
-
if (existsSync5(
|
|
14253
|
+
if (existsSync5(resolve16(ctx.base, to))) {
|
|
13303
14254
|
throw new DocmetaError(
|
|
13304
14255
|
`"${to}" already exists; the rename would overwrite it.`
|
|
13305
14256
|
);
|
|
@@ -13328,13 +14279,13 @@ function validateNewPath(p, base) {
|
|
|
13328
14279
|
const refuse = (why) => {
|
|
13329
14280
|
throw new DocmetaError(`"${p}" is not a usable path: ${why}.`);
|
|
13330
14281
|
};
|
|
13331
|
-
if (
|
|
14282
|
+
if (isAbsolute8(p)) refuse("it is absolute");
|
|
13332
14283
|
if (p.split(/[\\/]/).some((segment) => segment === "..")) {
|
|
13333
14284
|
refuse("it traverses upward");
|
|
13334
14285
|
}
|
|
13335
|
-
const abs =
|
|
13336
|
-
if (abs ===
|
|
13337
|
-
if (!abs.startsWith(
|
|
14286
|
+
const abs = resolve16(base, p);
|
|
14287
|
+
if (abs === resolve16(base)) refuse("it points at the corpus root, not a file");
|
|
14288
|
+
if (!abs.startsWith(resolve16(base) + sep8)) refuse("it escapes the corpus");
|
|
13338
14289
|
}
|
|
13339
14290
|
function refuseExternalMetadataWrites(changes, entries, ctx) {
|
|
13340
14291
|
const index = ctx.externalMetadata;
|
|
@@ -13363,7 +14314,7 @@ function refuseExternalMetadataWrites(changes, entries, ctx) {
|
|
|
13363
14314
|
continue;
|
|
13364
14315
|
}
|
|
13365
14316
|
if ("renamed" in c) {
|
|
13366
|
-
const entry = index.byPath.get(
|
|
14317
|
+
const entry = index.byPath.get(resolve16(ctx.base, c.file));
|
|
13367
14318
|
if (entry !== void 0 && entry.size > 0) {
|
|
13368
14319
|
const manifest = [...entry.values()][0]?.file ?? "manifest";
|
|
13369
14320
|
throw new DocmetaError(
|
|
@@ -13398,8 +14349,8 @@ function readableJoinValue(held, ctx) {
|
|
|
13398
14349
|
return opened.ok ? opened.value : held;
|
|
13399
14350
|
}
|
|
13400
14351
|
var sealedWrites = /* @__PURE__ */ new WeakMap();
|
|
13401
|
-
function writtenValue(
|
|
13402
|
-
const sealed = sealedWrites.get(
|
|
14352
|
+
function writtenValue(change, key, value) {
|
|
14353
|
+
const sealed = sealedWrites.get(change)?.get(key);
|
|
13403
14354
|
return sealed === void 0 ? value : sealed;
|
|
13404
14355
|
}
|
|
13405
14356
|
async function sealMarkedWrites(changes, entries, ctx) {
|
|
@@ -13501,14 +14452,14 @@ async function applyChanges(changes, entries, ctx, schemaPlan) {
|
|
|
13501
14452
|
const pendingWrites = [];
|
|
13502
14453
|
const pendingRenames = [];
|
|
13503
14454
|
for (const [label, ops] of grouped) {
|
|
13504
|
-
const path =
|
|
14455
|
+
const path = resolve16(ctx.base, label);
|
|
13505
14456
|
if (ops.renamedTo !== void 0) {
|
|
13506
14457
|
if (!existsSync5(path)) {
|
|
13507
14458
|
throw new DocmetaError(
|
|
13508
14459
|
`"${label}" is gone from disk since it was read; re-run the query.`
|
|
13509
14460
|
);
|
|
13510
14461
|
}
|
|
13511
|
-
const toDisk =
|
|
14462
|
+
const toDisk = resolve16(ctx.base, ops.renamedTo);
|
|
13512
14463
|
if (existsSync5(toDisk)) {
|
|
13513
14464
|
throw new DocmetaError(
|
|
13514
14465
|
`"${ops.renamedTo}" appeared on disk since the plan; re-run the query.`
|
|
@@ -13752,7 +14703,7 @@ function skipExpression(sql, from) {
|
|
|
13752
14703
|
|
|
13753
14704
|
// src/meta/commands/fill.ts
|
|
13754
14705
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
13755
|
-
import { resolve as
|
|
14706
|
+
import { resolve as resolve17, extname as extname7, join as join9 } from "node:path";
|
|
13756
14707
|
import {
|
|
13757
14708
|
completeValidatedJSON,
|
|
13758
14709
|
makeProvider,
|
|
@@ -13767,7 +14718,7 @@ import {
|
|
|
13767
14718
|
} from "@hawkeyexl/inference";
|
|
13768
14719
|
|
|
13769
14720
|
// src/meta/commands/fill-prompt.ts
|
|
13770
|
-
var FILL_PROMPT_VERSION =
|
|
14721
|
+
var FILL_PROMPT_VERSION = 4;
|
|
13771
14722
|
var DEFAULT_CHUNK_CHARS = 12e3;
|
|
13772
14723
|
function splitBody(body, chunkChars) {
|
|
13773
14724
|
if (body.length <= chunkChars) return [body];
|
|
@@ -13875,6 +14826,8 @@ function buildUserPrompt(params) {
|
|
|
13875
14826
|
}
|
|
13876
14827
|
|
|
13877
14828
|
// src/meta/commands/fill.ts
|
|
14829
|
+
var META_PROVENANCE_KEY = "meta-provenance";
|
|
14830
|
+
var PROVENANCE_KEYS = /* @__PURE__ */ new Set(["provenance", META_PROVENANCE_KEY]);
|
|
13878
14831
|
var DEFAULT_THRESHOLD = 0.7;
|
|
13879
14832
|
var DEFAULT_CONCURRENCY = 4;
|
|
13880
14833
|
var DEFAULT_PROVIDER = "auto";
|
|
@@ -14155,7 +15108,7 @@ async function runFill(opts) {
|
|
|
14155
15108
|
only
|
|
14156
15109
|
);
|
|
14157
15110
|
const managedSkips = proposed.filter((c) => managed.has(c.key)).map((c) => ({
|
|
14158
|
-
field:
|
|
15111
|
+
field: pointerOf(c.key),
|
|
14159
15112
|
required: c.required,
|
|
14160
15113
|
confidence: 0,
|
|
14161
15114
|
reasoning: "managed by derive",
|
|
@@ -14179,6 +15132,7 @@ async function runFill(opts) {
|
|
|
14179
15132
|
const inPlace = view.plain.flatMap((pointer) => {
|
|
14180
15133
|
const key = topLevelKeyOf(pointer);
|
|
14181
15134
|
if (key === void 0 || only !== void 0 && !only.has(key)) return [];
|
|
15135
|
+
if (PROVENANCE_KEYS.has(key)) return [];
|
|
14182
15136
|
if (externalMetadata?.owners.has(key) === true) return [];
|
|
14183
15137
|
if (candidates.some((c) => c.key === key)) return [];
|
|
14184
15138
|
return [{ key, pointer, value: extracted.data[key] }];
|
|
@@ -14222,7 +15176,11 @@ async function runFill(opts) {
|
|
|
14222
15176
|
sha256(content)
|
|
14223
15177
|
]);
|
|
14224
15178
|
const hit = candidates.length > 0 ? cache?.get(cacheKey) : void 0;
|
|
14225
|
-
const existing =
|
|
15179
|
+
const existing = Object.fromEntries(
|
|
15180
|
+
Object.entries(redactForModel(extracted.data, [...view.marked, ...probeMarks])).filter(
|
|
15181
|
+
([key]) => !PROVENANCE_KEYS.has(key)
|
|
15182
|
+
)
|
|
15183
|
+
);
|
|
14226
15184
|
let proposals = {};
|
|
14227
15185
|
if (hit) {
|
|
14228
15186
|
cachedCount++;
|
|
@@ -14375,7 +15333,8 @@ async function runFill(opts) {
|
|
|
14375
15333
|
...opts.includeContent ? { content } : {}
|
|
14376
15334
|
};
|
|
14377
15335
|
}
|
|
14378
|
-
const
|
|
15336
|
+
const plainPatch = patchOf(writable);
|
|
15337
|
+
const finalMarks = writable.length > 0 ? await validator.markedPointers({ ...view.data, ...plainPatch }, schemaSet) : /* @__PURE__ */ new Set();
|
|
14379
15338
|
const patch = {};
|
|
14380
15339
|
for (const f of writable) {
|
|
14381
15340
|
const key = keyOf(f);
|
|
@@ -14401,6 +15360,23 @@ async function runFill(opts) {
|
|
|
14401
15360
|
encrypted: true
|
|
14402
15361
|
});
|
|
14403
15362
|
}
|
|
15363
|
+
let metaProvenance;
|
|
15364
|
+
if (writable.length > 0) {
|
|
15365
|
+
const owner = externalMetadata?.owners.get(META_PROVENANCE_KEY)?.find((o) => members.includes(o.collection));
|
|
15366
|
+
const merged2 = owner === void 0 ? mergeMetaProvenance(extracted.data[META_PROVENANCE_KEY], identity.model, writable) : void 0;
|
|
15367
|
+
if (owner !== void 0) {
|
|
15368
|
+
metaProvenance = { written: false, skipReason: "manifest-owned", manifest: owner.file };
|
|
15369
|
+
} else if (merged2 === void 0 || await addsFindings(
|
|
15370
|
+
(data) => validator.validate(data, schemaSet, extracted.lineFor, extracted.colFor).then((errors) => settleFindings(errors, view, extracted)),
|
|
15371
|
+
{ ...view.data, ...plainPatch },
|
|
15372
|
+
merged2.list
|
|
15373
|
+
)) {
|
|
15374
|
+
metaProvenance = { written: false, skipReason: "schema-mismatch" };
|
|
15375
|
+
} else {
|
|
15376
|
+
patch[META_PROVENANCE_KEY] = merged2.list;
|
|
15377
|
+
metaProvenance = { written: true, entry: merged2.entry };
|
|
15378
|
+
}
|
|
15379
|
+
}
|
|
14404
15380
|
let next;
|
|
14405
15381
|
try {
|
|
14406
15382
|
next = extractor.apply(content, patch, {
|
|
@@ -14408,17 +15384,25 @@ async function runFill(opts) {
|
|
|
14408
15384
|
elements
|
|
14409
15385
|
});
|
|
14410
15386
|
} catch (err) {
|
|
14411
|
-
|
|
14412
|
-
label,
|
|
14413
|
-
|
|
14414
|
-
|
|
14415
|
-
|
|
14416
|
-
|
|
14417
|
-
|
|
15387
|
+
const withoutEntry = metaProvenance?.written === true ? tryApply(extractor, content, withoutKey(patch, META_PROVENANCE_KEY), {
|
|
15388
|
+
filePath: label,
|
|
15389
|
+
elements
|
|
15390
|
+
}) : void 0;
|
|
15391
|
+
if (withoutEntry === void 0) {
|
|
15392
|
+
return errorResult(
|
|
15393
|
+
label,
|
|
15394
|
+
extractor.name,
|
|
15395
|
+
errorMessage(err),
|
|
15396
|
+
schemaSet,
|
|
15397
|
+
fields
|
|
15398
|
+
);
|
|
15399
|
+
}
|
|
15400
|
+
next = withoutEntry;
|
|
15401
|
+
metaProvenance = { written: false, skipReason: "unwritable" };
|
|
14418
15402
|
}
|
|
14419
15403
|
const changed = next !== content;
|
|
14420
15404
|
if (changed && !dryRun && label !== "<stdin>") {
|
|
14421
|
-
await writeFileAtomic(
|
|
15405
|
+
await writeFileAtomic(resolve17(base, label), next);
|
|
14422
15406
|
}
|
|
14423
15407
|
return {
|
|
14424
15408
|
file: label,
|
|
@@ -14426,7 +15410,8 @@ async function runFill(opts) {
|
|
|
14426
15410
|
schemas: schemaSet,
|
|
14427
15411
|
fields,
|
|
14428
15412
|
changed,
|
|
14429
|
-
...opts.includeContent ? { content: next } : {}
|
|
15413
|
+
...opts.includeContent ? { content: next } : {},
|
|
15414
|
+
...metaProvenance === void 0 ? {} : { metaProvenance }
|
|
14430
15415
|
};
|
|
14431
15416
|
};
|
|
14432
15417
|
const results = [];
|
|
@@ -14440,7 +15425,7 @@ async function runFill(opts) {
|
|
|
14440
15425
|
);
|
|
14441
15426
|
}
|
|
14442
15427
|
const fileResults = await mapConcurrent(files, concurrency, async (file) => {
|
|
14443
|
-
const content = await readFile12(
|
|
15428
|
+
const content = await readFile12(resolve17(base, file), "utf8");
|
|
14444
15429
|
return processOne(file, content, extname7(file));
|
|
14445
15430
|
});
|
|
14446
15431
|
results.push(...fileResults);
|
|
@@ -14459,7 +15444,57 @@ function topKey(instancePath) {
|
|
|
14459
15444
|
const seg = instancePath.slice(1).split("/")[0] ?? "";
|
|
14460
15445
|
return seg.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
14461
15446
|
}
|
|
14462
|
-
var keyOf = (f) => f.field
|
|
15447
|
+
var keyOf = (f) => topKey(f.field);
|
|
15448
|
+
function isPlainRecord(value) {
|
|
15449
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15450
|
+
}
|
|
15451
|
+
function mergeMetaProvenance(held, model, written) {
|
|
15452
|
+
if (held !== void 0 && !Array.isArray(held)) return void 0;
|
|
15453
|
+
const list = held === void 0 ? [] : [...held];
|
|
15454
|
+
const at2 = list.findIndex((e) => isPlainRecord(e) && e["generated-by"] === model);
|
|
15455
|
+
const prior = list[at2];
|
|
15456
|
+
const kept = isPlainRecord(prior) ? prior : {};
|
|
15457
|
+
const fields = Array.isArray(kept.fields) ? kept.fields.filter((p) => typeof p === "string") : [];
|
|
15458
|
+
const confidence = {};
|
|
15459
|
+
if (isPlainRecord(kept.confidence)) {
|
|
15460
|
+
for (const [k, v] of Object.entries(kept.confidence)) {
|
|
15461
|
+
if (typeof v === "number") confidence[k] = v;
|
|
15462
|
+
}
|
|
15463
|
+
}
|
|
15464
|
+
for (const f of written) {
|
|
15465
|
+
if (!fields.includes(f.field)) fields.push(f.field);
|
|
15466
|
+
confidence[f.field] = f.confidence;
|
|
15467
|
+
}
|
|
15468
|
+
const entry = { ...kept, "generated-by": model, fields, confidence };
|
|
15469
|
+
if (at2 === -1) list.push(entry);
|
|
15470
|
+
else list[at2] = entry;
|
|
15471
|
+
return { list, entry };
|
|
15472
|
+
}
|
|
15473
|
+
async function addsFindings(validate, without, list) {
|
|
15474
|
+
const identity = (e) => JSON.stringify([e.schema, e.instancePath, e.keyword, e.subject ?? null, e.message]);
|
|
15475
|
+
const before = /* @__PURE__ */ new Map();
|
|
15476
|
+
for (const e of await validate(without)) {
|
|
15477
|
+
const key = identity(e);
|
|
15478
|
+
before.set(key, (before.get(key) ?? 0) + 1);
|
|
15479
|
+
}
|
|
15480
|
+
for (const e of await validate({ ...without, [META_PROVENANCE_KEY]: list })) {
|
|
15481
|
+
const key = identity(e);
|
|
15482
|
+
const left = before.get(key) ?? 0;
|
|
15483
|
+
if (left === 0) return true;
|
|
15484
|
+
before.set(key, left - 1);
|
|
15485
|
+
}
|
|
15486
|
+
return false;
|
|
15487
|
+
}
|
|
15488
|
+
function withoutKey(patch, key) {
|
|
15489
|
+
return Object.fromEntries(Object.entries(patch).filter(([k]) => k !== key));
|
|
15490
|
+
}
|
|
15491
|
+
function tryApply(extractor, content, patch, options) {
|
|
15492
|
+
try {
|
|
15493
|
+
return extractor.apply?.(content, patch, options);
|
|
15494
|
+
} catch {
|
|
15495
|
+
return void 0;
|
|
15496
|
+
}
|
|
15497
|
+
}
|
|
14463
15498
|
var IN_PLACE_REASONING = "The page held this value in plain text; its schema marks it x-manni-encrypt, so it was encrypted.";
|
|
14464
15499
|
var CIPHERTEXT_IN_TEXT = /~[A-Za-z0-9_-]{82,}/g;
|
|
14465
15500
|
function contentForModel(content, extractor, existing, held, supplied, options) {
|
|
@@ -14468,10 +15503,13 @@ function contentForModel(content, extractor, existing, held, supplied, options)
|
|
|
14468
15503
|
if (!Object.hasOwn(held, key) || supplied(pointerOf(key))) continue;
|
|
14469
15504
|
if (toJsonText(value) !== toJsonText(held[key])) patch[key] = value;
|
|
14470
15505
|
}
|
|
15506
|
+
const deletions = [...PROVENANCE_KEYS].filter(
|
|
15507
|
+
(key) => Object.hasOwn(held, key) && !supplied(pointerOf(key))
|
|
15508
|
+
);
|
|
14471
15509
|
let text2 = content;
|
|
14472
|
-
if (Object.keys(patch).length > 0 && extractor.apply !== void 0) {
|
|
15510
|
+
if ((Object.keys(patch).length > 0 || deletions.length > 0) && extractor.apply !== void 0) {
|
|
14473
15511
|
try {
|
|
14474
|
-
text2 = extractor.apply(content, patch, options);
|
|
15512
|
+
text2 = extractor.apply(content, patch, { ...options, deletions });
|
|
14475
15513
|
} catch {
|
|
14476
15514
|
}
|
|
14477
15515
|
}
|
|
@@ -14509,7 +15547,7 @@ function collectCandidates(schemas, data, errors, only) {
|
|
|
14509
15547
|
for (const [key, raw] of Object.entries(
|
|
14510
15548
|
properties
|
|
14511
15549
|
)) {
|
|
14512
|
-
if (key === FILE_SCHEMA_KEY) continue;
|
|
15550
|
+
if (key === FILE_SCHEMA_KEY || PROVENANCE_KEYS.has(key)) continue;
|
|
14513
15551
|
if (only && !only.has(key)) continue;
|
|
14514
15552
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) continue;
|
|
14515
15553
|
const present = Object.prototype.hasOwnProperty.call(data, key);
|
|
@@ -14675,7 +15713,7 @@ function gate(candidates, proposals, threshold) {
|
|
|
14675
15713
|
const proposal = proposals[c.key];
|
|
14676
15714
|
if (proposal == null) {
|
|
14677
15715
|
return {
|
|
14678
|
-
field:
|
|
15716
|
+
field: pointerOf(c.key),
|
|
14679
15717
|
required: c.required,
|
|
14680
15718
|
confidence: 0,
|
|
14681
15719
|
reasoning: "",
|
|
@@ -14685,7 +15723,7 @@ function gate(candidates, proposals, threshold) {
|
|
|
14685
15723
|
}
|
|
14686
15724
|
const passes = proposal.confidence >= threshold;
|
|
14687
15725
|
return {
|
|
14688
|
-
field:
|
|
15726
|
+
field: pointerOf(c.key),
|
|
14689
15727
|
required: c.required,
|
|
14690
15728
|
confidence: proposal.confidence,
|
|
14691
15729
|
reasoning: proposal.reasoning,
|
|
@@ -14784,10 +15822,434 @@ async function mapConcurrent(items2, limit, fn) {
|
|
|
14784
15822
|
|
|
14785
15823
|
// src/meta/commands/derive.ts
|
|
14786
15824
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
14787
|
-
import { extname as extname8, resolve as
|
|
15825
|
+
import { extname as extname8, relative as relative13, resolve as resolve18, sep as sep9 } from "node:path";
|
|
15826
|
+
|
|
15827
|
+
// src/meta/core/external-metadata-write.ts
|
|
15828
|
+
import { posix as posix3 } from "node:path";
|
|
15829
|
+
import {
|
|
15830
|
+
LineCounter as LineCounter3,
|
|
15831
|
+
isMap as isMap6,
|
|
15832
|
+
isNode as isNode2,
|
|
15833
|
+
isPair,
|
|
15834
|
+
isScalar as isScalar4,
|
|
15835
|
+
isSeq as isSeq4,
|
|
15836
|
+
parseDocument as parseDocument7,
|
|
15837
|
+
stringify as stringify3
|
|
15838
|
+
} from "yaml";
|
|
15839
|
+
function spliceManifestValue(text2, options) {
|
|
15840
|
+
const { entry, key, value, file } = options;
|
|
15841
|
+
const join10 = options.join ?? PATH_JOIN;
|
|
15842
|
+
const at2 = (line2) => {
|
|
15843
|
+
const name2 = file === void 0 ? "Manifest" : `Manifest ${file}`;
|
|
15844
|
+
if (line2 === void 0) return name2;
|
|
15845
|
+
return file === void 0 ? `${name2} line ${String(line2)}` : `${name2}:${String(line2)}`;
|
|
15846
|
+
};
|
|
15847
|
+
if (key === FILE_SCHEMA_KEY) {
|
|
15848
|
+
throw new DocmetaError(
|
|
15849
|
+
`${at2()}: refusing to write "${FILE_SCHEMA_KEY}" for "${entry}"; a manifest never chooses the schema a document is judged by. Use "overrides" in the config.`
|
|
15850
|
+
);
|
|
15851
|
+
}
|
|
15852
|
+
if (value === void 0) {
|
|
15853
|
+
throw new DocmetaError(`${at2()}: refusing to write "${key}" for "${entry}" with no value.`);
|
|
15854
|
+
}
|
|
15855
|
+
const eol = detectEol(text2);
|
|
15856
|
+
const lc = new LineCounter3();
|
|
15857
|
+
const doc = parseDocument7(text2, { lineCounter: lc, uniqueKeys: false });
|
|
15858
|
+
const problem = doc.errors[0];
|
|
15859
|
+
if (problem) {
|
|
15860
|
+
throw new DocmetaError(`${at2()} is not valid YAML: ${problem.message}`);
|
|
15861
|
+
}
|
|
15862
|
+
const keyLine = (node) => {
|
|
15863
|
+
const r = rangeOf(node);
|
|
15864
|
+
return r ? lc.linePos(r[0]).line : void 0;
|
|
15865
|
+
};
|
|
15866
|
+
const same = join10 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
|
|
15867
|
+
const root = doc.contents;
|
|
15868
|
+
let spelled = entry;
|
|
15869
|
+
let edit;
|
|
15870
|
+
if (root === null || isScalar4(root) && root.value === null && emptySource(text2, root)) {
|
|
15871
|
+
edit = startManifest(text2, entry, key, value, eol);
|
|
15872
|
+
} else if (!isMap6(root)) {
|
|
15873
|
+
throw new DocmetaError(
|
|
15874
|
+
`${at2()}: the manifest must be a mapping from document ${join10 === PATH_JOIN ? "path" : `"${join10}"`} to owned keys.`
|
|
15875
|
+
);
|
|
15876
|
+
} else {
|
|
15877
|
+
const style = detectStyle(text2, root, eol);
|
|
15878
|
+
const matches = root.items.filter((p) => same(keyString(p.key), entry));
|
|
15879
|
+
const [first, second] = matches;
|
|
15880
|
+
if (first && second) {
|
|
15881
|
+
throw new DocmetaError(
|
|
15882
|
+
`${at2(keyLine(second.key))}: "${keyString(second.key)}" is named twice (first at line ${String(keyLine(first.key) ?? "?")}). Merge the two entries into one.`
|
|
15883
|
+
);
|
|
15884
|
+
}
|
|
15885
|
+
if (first === void 0) {
|
|
15886
|
+
if (root.flow === true && root.items.length > 0) {
|
|
15887
|
+
throw new DocmetaError(
|
|
15888
|
+
`${at2()}: the manifest is a flow mapping ({ \u2026 }), which this writer does not add entries to. Rewrite it in block style.`
|
|
15889
|
+
);
|
|
15890
|
+
}
|
|
15891
|
+
edit = appendEntry(text2, root, entry, key, value, style);
|
|
15892
|
+
} else {
|
|
15893
|
+
spelled = keyString(first.key);
|
|
15894
|
+
const where = at2(keyLine(first.key));
|
|
15895
|
+
const v = first.value;
|
|
15896
|
+
if (!isMap6(v)) {
|
|
15897
|
+
throw new DocmetaError(`${where}: "${spelled}" must be a mapping of owned keys to values.`);
|
|
15898
|
+
}
|
|
15899
|
+
const hits = v.items.filter((kv) => keyString(kv.key) === key);
|
|
15900
|
+
const [hit, again] = hits;
|
|
15901
|
+
if (hit && again) {
|
|
15902
|
+
throw new DocmetaError(
|
|
15903
|
+
`${at2(keyLine(again.key))}: "${spelled}" sets "${key}" twice (first at line ${String(keyLine(hit.key) ?? "?")}). Remove one.`
|
|
15904
|
+
);
|
|
15905
|
+
}
|
|
15906
|
+
const flowRefusal = () => new DocmetaError(
|
|
15907
|
+
`${where}: "${spelled}" is a flow mapping ({ \u2026 }), which this writer does not add keys to. Rewrite the entry in block style.`
|
|
15908
|
+
);
|
|
15909
|
+
const formRefusal = () => new DocmetaError(
|
|
15910
|
+
`${where}: "${spelled}" writes a key in a form this writer does not edit. Write it as "${key}: \u2026".`
|
|
15911
|
+
);
|
|
15912
|
+
let made;
|
|
15913
|
+
if (v.flow === true) {
|
|
15914
|
+
if (hit) made = replaceFlowValue(text2, hit, value);
|
|
15915
|
+
else if (v.items.length === 0) made = fillEmptyEntry(text2, first, key, value, style);
|
|
15916
|
+
else throw flowRefusal();
|
|
15917
|
+
if (!made) throw hit ? flowRefusal() : formRefusal();
|
|
15918
|
+
} else {
|
|
15919
|
+
made = hit ? replaceBlockValue(text2, hit, value, style) : insertKey(text2, v, key, value, style);
|
|
15920
|
+
if (!made) throw formRefusal();
|
|
15921
|
+
}
|
|
15922
|
+
edit = made;
|
|
15923
|
+
}
|
|
15924
|
+
}
|
|
15925
|
+
const out = text2.slice(0, edit.start) + edit.insert + text2.slice(edit.end);
|
|
15926
|
+
const line = readBack(
|
|
15927
|
+
doc,
|
|
15928
|
+
out,
|
|
15929
|
+
spelled,
|
|
15930
|
+
key,
|
|
15931
|
+
value,
|
|
15932
|
+
(why) => new DocmetaError(
|
|
15933
|
+
`${at2()}: writing "${key}" for "${entry}" did not read back as written, so nothing was written. ${why}`
|
|
15934
|
+
)
|
|
15935
|
+
);
|
|
15936
|
+
return { text: out, line };
|
|
15937
|
+
}
|
|
15938
|
+
function removeManifestKey(text2, options) {
|
|
15939
|
+
const { entry, key, file } = options;
|
|
15940
|
+
const join10 = options.join ?? PATH_JOIN;
|
|
15941
|
+
const at2 = (line) => {
|
|
15942
|
+
const name2 = file === void 0 ? "Manifest" : `Manifest ${file}`;
|
|
15943
|
+
if (line === void 0) return name2;
|
|
15944
|
+
return file === void 0 ? `${name2} line ${String(line)}` : `${name2}:${String(line)}`;
|
|
15945
|
+
};
|
|
15946
|
+
const eol = detectEol(text2);
|
|
15947
|
+
const lc = new LineCounter3();
|
|
15948
|
+
const doc = parseDocument7(text2, { lineCounter: lc, uniqueKeys: false });
|
|
15949
|
+
const problem = doc.errors[0];
|
|
15950
|
+
if (problem) {
|
|
15951
|
+
throw new DocmetaError(`${at2()} is not valid YAML: ${problem.message}`);
|
|
15952
|
+
}
|
|
15953
|
+
const keyLine = (node) => {
|
|
15954
|
+
const r = rangeOf(node);
|
|
15955
|
+
return r ? lc.linePos(r[0]).line : void 0;
|
|
15956
|
+
};
|
|
15957
|
+
const root = doc.contents;
|
|
15958
|
+
if (root === null || isScalar4(root) && root.value === null) return { text: text2 };
|
|
15959
|
+
if (!isMap6(root)) {
|
|
15960
|
+
throw new DocmetaError(
|
|
15961
|
+
`${at2()}: the manifest must be a mapping from document ${join10 === PATH_JOIN ? "path" : `"${join10}"`} to owned keys.`
|
|
15962
|
+
);
|
|
15963
|
+
}
|
|
15964
|
+
const same = join10 === PATH_JOIN ? (a, b) => posix3.normalize(a) === posix3.normalize(b) : (a, b) => a === b;
|
|
15965
|
+
const matches = root.items.filter((p) => same(keyString(p.key), entry));
|
|
15966
|
+
const [first, second] = matches;
|
|
15967
|
+
if (first && second) {
|
|
15968
|
+
throw new DocmetaError(
|
|
15969
|
+
`${at2(keyLine(second.key))}: "${keyString(second.key)}" is named twice (first at line ${String(keyLine(first.key) ?? "?")}). Merge the two entries into one.`
|
|
15970
|
+
);
|
|
15971
|
+
}
|
|
15972
|
+
if (first === void 0) return { text: text2 };
|
|
15973
|
+
const spelled = keyString(first.key);
|
|
15974
|
+
const where = at2(keyLine(first.key));
|
|
15975
|
+
const v = first.value;
|
|
15976
|
+
if (!isMap6(v)) {
|
|
15977
|
+
throw new DocmetaError(`${where}: "${spelled}" must be a mapping of owned keys to values.`);
|
|
15978
|
+
}
|
|
15979
|
+
const hits = v.items.filter((kv) => keyString(kv.key) === key);
|
|
15980
|
+
const [hit, again] = hits;
|
|
15981
|
+
if (hit && again) {
|
|
15982
|
+
throw new DocmetaError(
|
|
15983
|
+
`${at2(keyLine(again.key))}: "${spelled}" sets "${key}" twice (first at line ${String(keyLine(hit.key) ?? "?")}). Remove one.`
|
|
15984
|
+
);
|
|
15985
|
+
}
|
|
15986
|
+
if (hit === void 0) return { text: text2 };
|
|
15987
|
+
const whole = v.items.length === 1;
|
|
15988
|
+
if (!whole && v.flow === true) {
|
|
15989
|
+
throw new DocmetaError(
|
|
15990
|
+
`${where}: "${spelled}" is a flow mapping ({ \u2026 }), which this writer does not remove keys from. Rewrite the entry in block style.`
|
|
15991
|
+
);
|
|
15992
|
+
}
|
|
15993
|
+
const from = rangeOf(whole ? first.key : hit.key);
|
|
15994
|
+
const to = whole ? contentEnd(text2, v) : contentEnd(text2, hit.value) ?? contentEnd(text2, hit.key);
|
|
15995
|
+
if (!from || to === void 0) {
|
|
15996
|
+
throw new DocmetaError(
|
|
15997
|
+
`${where}: "${spelled}" writes a key in a form this writer does not edit. Write it as "${key}: \u2026".`
|
|
15998
|
+
);
|
|
15999
|
+
}
|
|
16000
|
+
let start = text2.lastIndexOf("\n", from[0] - 1) + 1;
|
|
16001
|
+
let end = lineEnd(text2, to);
|
|
16002
|
+
if (text2.startsWith(eol, end)) end += eol.length;
|
|
16003
|
+
else if (start >= eol.length) start -= eol.length;
|
|
16004
|
+
if (whole) {
|
|
16005
|
+
if (text2.slice(start - 2 * eol.length, start) === eol + eol) start -= eol.length;
|
|
16006
|
+
else if (start === 0 && text2.startsWith(eol, end)) end += eol.length;
|
|
16007
|
+
}
|
|
16008
|
+
const out = text2.slice(0, start) + text2.slice(end);
|
|
16009
|
+
const after = parseDocument7(out, { uniqueKeys: false });
|
|
16010
|
+
const fail2 = (why) => new DocmetaError(
|
|
16011
|
+
`${at2()}: removing "${key}" for "${entry}" did not read back as removed, so nothing was written. ${why}`
|
|
16012
|
+
);
|
|
16013
|
+
if (after.errors[0]) throw fail2(`The result is not valid YAML: ${after.errors[0].message}`);
|
|
16014
|
+
const was = doc.toJS({ maxAliasCount: 100 });
|
|
16015
|
+
const wasRoot = isRecord5(was) ? was : {};
|
|
16016
|
+
const wasEntry = wasRoot[spelled];
|
|
16017
|
+
const rest = Object.fromEntries(
|
|
16018
|
+
Object.entries(isRecord5(wasEntry) ? wasEntry : {}).filter(([k]) => k !== key)
|
|
16019
|
+
);
|
|
16020
|
+
const kept = Object.entries(wasRoot).filter(([k]) => k !== spelled);
|
|
16021
|
+
if (Object.keys(rest).length > 0) kept.push([spelled, rest]);
|
|
16022
|
+
const now = after.toJS({ maxAliasCount: 100 });
|
|
16023
|
+
const expected = kept.length === 0 ? null : Object.fromEntries(kept);
|
|
16024
|
+
if (!deepEqual(now ?? null, expected)) throw fail2("Another value in the manifest would have changed.");
|
|
16025
|
+
return { text: out };
|
|
16026
|
+
}
|
|
16027
|
+
function startManifest(text2, entry, key, value, eol) {
|
|
16028
|
+
const style = { step: 2, indentSeq: true, eol };
|
|
16029
|
+
const block = blockLines({ [entry]: { [key]: value } }, 0, style);
|
|
16030
|
+
let insert;
|
|
16031
|
+
if (text2 === "" || text2.endsWith("\n")) insert = block + eol;
|
|
16032
|
+
else insert = eol + block;
|
|
16033
|
+
return { start: text2.length, end: text2.length, insert };
|
|
16034
|
+
}
|
|
16035
|
+
function appendEntry(text2, root, entry, key, value, style) {
|
|
16036
|
+
const body = { [entry]: { [key]: value } };
|
|
16037
|
+
if (root.flow === true) {
|
|
16038
|
+
const r = rangeOf(root);
|
|
16039
|
+
const start = r ? r[0] : text2.length;
|
|
16040
|
+
const end = contentEnd(text2, root) ?? start;
|
|
16041
|
+
return { start, end, insert: blockLines(body, colOf(text2, start), style) };
|
|
16042
|
+
}
|
|
16043
|
+
const firstKey = rangeOf(root.items[0]?.key);
|
|
16044
|
+
const col = firstKey ? colOf(text2, firstKey[0]) : 0;
|
|
16045
|
+
const pos = lineEnd(text2, contentEnd(text2, root) ?? text2.length);
|
|
16046
|
+
const last = root.items.at(-1);
|
|
16047
|
+
const spaced = root.items.length > 1 && isNode2(last?.key) && last.key.spaceBefore === true;
|
|
16048
|
+
return {
|
|
16049
|
+
start: pos,
|
|
16050
|
+
end: pos,
|
|
16051
|
+
insert: style.eol + (spaced ? style.eol : "") + blockLines(body, col, style)
|
|
16052
|
+
};
|
|
16053
|
+
}
|
|
16054
|
+
function insertKey(text2, entryMap, key, value, style) {
|
|
16055
|
+
const firstKey = rangeOf(entryMap.items[0]?.key);
|
|
16056
|
+
const end = contentEnd(text2, entryMap);
|
|
16057
|
+
if (!firstKey || end === void 0) return void 0;
|
|
16058
|
+
const pos = lineEnd(text2, end);
|
|
16059
|
+
return {
|
|
16060
|
+
start: pos,
|
|
16061
|
+
end: pos,
|
|
16062
|
+
insert: style.eol + blockLines({ [key]: value }, colOf(text2, firstKey[0]), style)
|
|
16063
|
+
};
|
|
16064
|
+
}
|
|
16065
|
+
function fillEmptyEntry(text2, entryPair, key, value, style) {
|
|
16066
|
+
const start = colonEnd(text2, entryPair);
|
|
16067
|
+
const keyRange = rangeOf(entryPair.key);
|
|
16068
|
+
const end = contentEnd(text2, entryPair.value);
|
|
16069
|
+
if (start === void 0 || !keyRange || end === void 0) return void 0;
|
|
16070
|
+
const col = colOf(text2, keyRange[0]) + style.step;
|
|
16071
|
+
return { start, end, insert: style.eol + blockLines({ [key]: value }, col, style) };
|
|
16072
|
+
}
|
|
16073
|
+
function replaceBlockValue(text2, pair, value, style) {
|
|
16074
|
+
const colon = colonEnd(text2, pair);
|
|
16075
|
+
const keyRange = rangeOf(pair.key);
|
|
16076
|
+
if (colon === void 0 || !keyRange) return void 0;
|
|
16077
|
+
const keyCol = colOf(text2, keyRange[0]);
|
|
16078
|
+
let start = colon;
|
|
16079
|
+
let ownLine = false;
|
|
16080
|
+
let i = colon;
|
|
16081
|
+
while (text2.charAt(i) === " " || text2.charAt(i) === " ") i++;
|
|
16082
|
+
if (text2.charAt(i) === "#") {
|
|
16083
|
+
start = lineEnd(text2, i);
|
|
16084
|
+
ownLine = true;
|
|
16085
|
+
}
|
|
16086
|
+
const old = pair.value;
|
|
16087
|
+
const oldEnd = isScalar4(old) && old.value === null && emptySource(text2, old) ? start : contentEnd(text2, old) ?? start;
|
|
16088
|
+
const end = Math.max(start, oldEnd);
|
|
16089
|
+
let insert = afterColon(value, keyCol, style);
|
|
16090
|
+
if (ownLine && !insert.startsWith(style.eol)) {
|
|
16091
|
+
insert = style.eol + " ".repeat(keyCol + style.step) + insert.trimStart();
|
|
16092
|
+
}
|
|
16093
|
+
return { start, end, insert };
|
|
16094
|
+
}
|
|
16095
|
+
function replaceFlowValue(text2, pair, value) {
|
|
16096
|
+
const r = rangeOf(pair.value);
|
|
16097
|
+
if (!r || emptySource(text2, pair.value)) return void 0;
|
|
16098
|
+
const end = contentEnd(text2, pair.value) ?? r[1];
|
|
16099
|
+
return { start: r[0], end, insert: flowText(value) };
|
|
16100
|
+
}
|
|
16101
|
+
function readBack(before, out, spelled, key, value, fail2) {
|
|
16102
|
+
const lc = new LineCounter3();
|
|
16103
|
+
const after = parseDocument7(out, { lineCounter: lc, uniqueKeys: false });
|
|
16104
|
+
const problem = after.errors[0];
|
|
16105
|
+
if (problem) throw fail2(`The result is not valid YAML: ${problem.message}`);
|
|
16106
|
+
const was = before.toJS({ maxAliasCount: 100 });
|
|
16107
|
+
const now = after.toJS({ maxAliasCount: 100 });
|
|
16108
|
+
const wasRoot = isRecord5(was) ? was : {};
|
|
16109
|
+
const wasEntry = wasRoot[spelled];
|
|
16110
|
+
const expected = {
|
|
16111
|
+
...wasRoot,
|
|
16112
|
+
[spelled]: { ...isRecord5(wasEntry) ? wasEntry : {}, [key]: value }
|
|
16113
|
+
};
|
|
16114
|
+
const nowEntry = isRecord5(now) ? now[spelled] : void 0;
|
|
16115
|
+
if (!isRecord5(nowEntry) || !deepEqual(nowEntry[key], value)) {
|
|
16116
|
+
throw fail2(
|
|
16117
|
+
"The value reads back differently. A Date, an undefined member, or another value YAML cannot hold does not survive the trip."
|
|
16118
|
+
);
|
|
16119
|
+
}
|
|
16120
|
+
if (!deepEqual(now, expected)) {
|
|
16121
|
+
throw fail2("Another value in the manifest would have changed.");
|
|
16122
|
+
}
|
|
16123
|
+
const root = after.contents;
|
|
16124
|
+
const pair = isMap6(root) ? root.items.find((p) => keyString(p.key) === spelled) : void 0;
|
|
16125
|
+
const entryMap = pair?.value;
|
|
16126
|
+
const kv = isMap6(entryMap) ? entryMap.items.find((p) => keyString(p.key) === key) : void 0;
|
|
16127
|
+
const r = rangeOf(kv?.value) ?? rangeOf(kv?.key);
|
|
16128
|
+
if (!r) throw fail2("The written value could not be found again.");
|
|
16129
|
+
return lc.linePos(r[0]).line;
|
|
16130
|
+
}
|
|
16131
|
+
function blockLines(pair, col, style) {
|
|
16132
|
+
const s = stringify3(pair, { lineWidth: 0, indent: style.step, indentSeq: style.indentSeq });
|
|
16133
|
+
return indentLines(s.replace(/\n$/, "").split("\n"), col, 0).join(style.eol);
|
|
16134
|
+
}
|
|
16135
|
+
function afterColon(value, col, style) {
|
|
16136
|
+
const s = stringify3({ k: value }, { lineWidth: 0, indent: style.step, indentSeq: style.indentSeq });
|
|
16137
|
+
const lines = s.replace(/\n$/, "").slice("k:".length).split("\n");
|
|
16138
|
+
return indentLines(lines, col, 1).join(style.eol);
|
|
16139
|
+
}
|
|
16140
|
+
function indentLines(lines, col, from) {
|
|
16141
|
+
const pad2 = " ".repeat(col);
|
|
16142
|
+
return lines.map((l, i) => i < from || l === "" ? l : pad2 + l);
|
|
16143
|
+
}
|
|
16144
|
+
function flowText(value) {
|
|
16145
|
+
const s = stringify3(value, { lineWidth: 0, collectionStyle: "flow" }).replace(/\n$/, "");
|
|
16146
|
+
return s.includes("\n") ? JSON.stringify(value) : s;
|
|
16147
|
+
}
|
|
16148
|
+
function detectEol(text2) {
|
|
16149
|
+
const i = text2.indexOf("\n");
|
|
16150
|
+
return i > 0 && text2.charAt(i - 1) === "\r" ? "\r\n" : "\n";
|
|
16151
|
+
}
|
|
16152
|
+
function detectStyle(text2, root, eol) {
|
|
16153
|
+
let step = 2;
|
|
16154
|
+
for (const pair of root.items) {
|
|
16155
|
+
const v = pair.value;
|
|
16156
|
+
const entryKey = rangeOf(pair.key);
|
|
16157
|
+
const ownedKey = isMap6(v) && v.flow !== true ? rangeOf(v.items[0]?.key) : void 0;
|
|
16158
|
+
if (entryKey && ownedKey) {
|
|
16159
|
+
const d = colOf(text2, ownedKey[0]) - colOf(text2, entryKey[0]);
|
|
16160
|
+
if (d > 0) {
|
|
16161
|
+
step = d;
|
|
16162
|
+
break;
|
|
16163
|
+
}
|
|
16164
|
+
}
|
|
16165
|
+
}
|
|
16166
|
+
return { step, indentSeq: seqIndented(text2, root) ?? true, eol };
|
|
16167
|
+
}
|
|
16168
|
+
function seqIndented(text2, node) {
|
|
16169
|
+
if (isMap6(node)) {
|
|
16170
|
+
for (const pair of node.items) {
|
|
16171
|
+
const v = pair.value;
|
|
16172
|
+
if (isSeq4(v) && v.flow !== true && v.items.length > 0) {
|
|
16173
|
+
const k = rangeOf(pair.key);
|
|
16174
|
+
const s = rangeOf(v);
|
|
16175
|
+
if (k && s) return colOf(text2, s[0]) > colOf(text2, k[0]);
|
|
16176
|
+
}
|
|
16177
|
+
const inner = seqIndented(text2, v);
|
|
16178
|
+
if (inner !== void 0) return inner;
|
|
16179
|
+
}
|
|
16180
|
+
} else if (isSeq4(node)) {
|
|
16181
|
+
for (const item of node.items) {
|
|
16182
|
+
const inner = seqIndented(text2, item);
|
|
16183
|
+
if (inner !== void 0) return inner;
|
|
16184
|
+
}
|
|
16185
|
+
}
|
|
16186
|
+
return void 0;
|
|
16187
|
+
}
|
|
16188
|
+
function contentEnd(text2, node) {
|
|
16189
|
+
if (isMap6(node) && node.flow !== true) {
|
|
16190
|
+
const last = node.items.at(-1);
|
|
16191
|
+
if (last) return contentEnd(text2, last.value) ?? contentEnd(text2, last.key);
|
|
16192
|
+
} else if (isSeq4(node) && node.flow !== true) {
|
|
16193
|
+
const last = node.items.at(-1);
|
|
16194
|
+
if (last !== void 0) {
|
|
16195
|
+
return isPair(last) ? contentEnd(text2, last.value) ?? contentEnd(text2, last.key) : contentEnd(text2, last);
|
|
16196
|
+
}
|
|
16197
|
+
}
|
|
16198
|
+
const r = rangeOf(node);
|
|
16199
|
+
if (!r) return void 0;
|
|
16200
|
+
let end = r[1];
|
|
16201
|
+
while (end > r[0] && /\s/.test(text2.charAt(end - 1))) end--;
|
|
16202
|
+
return end;
|
|
16203
|
+
}
|
|
16204
|
+
function colonEnd(text2, pair) {
|
|
16205
|
+
const r = rangeOf(pair.key);
|
|
16206
|
+
if (!r) return void 0;
|
|
16207
|
+
let i = r[1];
|
|
16208
|
+
while (text2.charAt(i) === " " || text2.charAt(i) === " ") i++;
|
|
16209
|
+
return text2.charAt(i) === ":" ? i + 1 : void 0;
|
|
16210
|
+
}
|
|
16211
|
+
function lineEnd(text2, i) {
|
|
16212
|
+
const j = text2.indexOf("\n", i);
|
|
16213
|
+
if (j < 0) return text2.length;
|
|
16214
|
+
return j > 0 && text2.charAt(j - 1) === "\r" ? j - 1 : j;
|
|
16215
|
+
}
|
|
16216
|
+
function colOf(text2, offset) {
|
|
16217
|
+
if (offset === 0) return 0;
|
|
16218
|
+
let start = text2.lastIndexOf("\n", offset - 1) + 1;
|
|
16219
|
+
if (start === 0 && text2.startsWith("\uFEFF")) start = 1;
|
|
16220
|
+
return offset - start;
|
|
16221
|
+
}
|
|
16222
|
+
function rangeOf(node) {
|
|
16223
|
+
return isNode2(node) ? node.range ?? void 0 : void 0;
|
|
16224
|
+
}
|
|
16225
|
+
function emptySource(text2, node) {
|
|
16226
|
+
const r = rangeOf(node);
|
|
16227
|
+
return !r || text2.slice(r[0], r[1]).trim() === "";
|
|
16228
|
+
}
|
|
16229
|
+
function keyString(key) {
|
|
16230
|
+
return isScalar4(key) ? String(key.value) : String(key);
|
|
16231
|
+
}
|
|
16232
|
+
function isRecord5(x) {
|
|
16233
|
+
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
16234
|
+
}
|
|
16235
|
+
|
|
16236
|
+
// src/meta/commands/derive.ts
|
|
16237
|
+
var GENERATED_BY_ENV = "MANNI_GENERATED_BY";
|
|
14788
16238
|
var SOURCE_HINT = "narrow --sources or --fields";
|
|
14789
16239
|
async function runDerive(opts) {
|
|
14790
16240
|
const cwd = opts.cwd ?? process.cwd();
|
|
16241
|
+
const targets = [];
|
|
16242
|
+
const typedInputs = opts.inputs.map((arg) => {
|
|
16243
|
+
if (arg === STDIN_TOKEN) return arg;
|
|
16244
|
+
const { page, lines } = parseProvenanceTarget(arg);
|
|
16245
|
+
if (lines === void 0) return arg;
|
|
16246
|
+
if (page === STDIN_TOKEN) return STDIN_TOKEN;
|
|
16247
|
+
targets.push({ target: arg, page });
|
|
16248
|
+
return page;
|
|
16249
|
+
});
|
|
16250
|
+
const env = opts.env ?? process.env;
|
|
16251
|
+
const flagGeneratedBy = machineName(opts.generatedBy);
|
|
16252
|
+
const generatedBy = opts.generatedBy !== void 0 ? flagGeneratedBy : machineName(env[GENERATED_BY_ENV]);
|
|
14791
16253
|
const {
|
|
14792
16254
|
config,
|
|
14793
16255
|
inputs,
|
|
@@ -14801,7 +16263,7 @@ async function runDerive(opts) {
|
|
|
14801
16263
|
cwd,
|
|
14802
16264
|
configPath: opts.configPath,
|
|
14803
16265
|
noConfig: opts.noConfig,
|
|
14804
|
-
inputs:
|
|
16266
|
+
inputs: typedInputs,
|
|
14805
16267
|
...opts.collections !== void 0 ? { collections: opts.collections } : {},
|
|
14806
16268
|
onConfigLoaded: opts.onConfigLoaded
|
|
14807
16269
|
});
|
|
@@ -14815,7 +16277,20 @@ async function runDerive(opts) {
|
|
|
14815
16277
|
}
|
|
14816
16278
|
const commands = commandsOf(config?.derive);
|
|
14817
16279
|
const fields = resolveFields(opts.fields, config?.derive?.fields, commands);
|
|
16280
|
+
const wantsProvenance = fields.includes(PROVENANCE_FIELD);
|
|
16281
|
+
const firstTarget = targets[0];
|
|
16282
|
+
if (firstTarget !== void 0 && generatedBy === void 0) {
|
|
16283
|
+
throw new DocmetaError(
|
|
16284
|
+
`${firstTarget.target} names lines, which only --generated-by uses. Pass --generated-by, or drop the range.`
|
|
16285
|
+
);
|
|
16286
|
+
}
|
|
16287
|
+
if ((flagGeneratedBy !== void 0 || firstTarget !== void 0) && !wantsProvenance) {
|
|
16288
|
+
throw new DocmetaError(
|
|
16289
|
+
"--generated-by attributes provenance, which is not in --fields. Add provenance, or drop --generated-by."
|
|
16290
|
+
);
|
|
16291
|
+
}
|
|
14818
16292
|
for (const field of fields) {
|
|
16293
|
+
if (field === PROVENANCE_FIELD) continue;
|
|
14819
16294
|
const owner = manifestOwning(field, declaredCollections);
|
|
14820
16295
|
if (owner !== void 0) {
|
|
14821
16296
|
throw new DocmetaError(
|
|
@@ -14878,6 +16353,18 @@ async function runDerive(opts) {
|
|
|
14878
16353
|
})
|
|
14879
16354
|
});
|
|
14880
16355
|
const files = fromCollections ? retainMembers(walked, membersFor) : walked;
|
|
16356
|
+
const rangesByPage = /* @__PURE__ */ new Map();
|
|
16357
|
+
for (const t of targets) {
|
|
16358
|
+
const label = relative13(base, resolve18(base, t.page)).split(sep9).join("/");
|
|
16359
|
+
if (!files.includes(label)) {
|
|
16360
|
+
throw new DocmetaError(
|
|
16361
|
+
`${t.target} does not name one file that derive reads; a range names lines of one file.`
|
|
16362
|
+
);
|
|
16363
|
+
}
|
|
16364
|
+
const named = rangesByPage.get(label) ?? [];
|
|
16365
|
+
if (!named.includes(t.target)) named.push(t.target);
|
|
16366
|
+
rangesByPage.set(label, named);
|
|
16367
|
+
}
|
|
14881
16368
|
assertNonEmpty({
|
|
14882
16369
|
files,
|
|
14883
16370
|
inputs,
|
|
@@ -14891,7 +16378,7 @@ async function runDerive(opts) {
|
|
|
14891
16378
|
const loaded = /* @__PURE__ */ new Map();
|
|
14892
16379
|
const errors = /* @__PURE__ */ new Map();
|
|
14893
16380
|
for (const label of files) {
|
|
14894
|
-
const absPath =
|
|
16381
|
+
const absPath = resolve18(base, label);
|
|
14895
16382
|
const extractor = forcedExtractor ?? extractorForExtension(extname8(label));
|
|
14896
16383
|
if (!extractor) {
|
|
14897
16384
|
errors.set(label, {
|
|
@@ -14916,6 +16403,38 @@ async function runDerive(opts) {
|
|
|
14916
16403
|
errors.set(label, { format: extractor.name, message: errorMessage(err) });
|
|
14917
16404
|
}
|
|
14918
16405
|
}
|
|
16406
|
+
const provenanceRoot = configDir ?? cwd;
|
|
16407
|
+
const manifests = wantsProvenance ? provenanceManifests(declaredCollections, provenanceRoot, base) : [];
|
|
16408
|
+
const provenanceCollections = declaredCollections.map((c) => ({
|
|
16409
|
+
...c,
|
|
16410
|
+
externalMetadata: c.externalMetadata.filter((m) => m.keys.includes(PROVENANCE_FIELD))
|
|
16411
|
+
})).filter((c) => c.externalMetadata.length > 0);
|
|
16412
|
+
const provenanceIndex = manifests.length === 0 ? null : await loadExternalMetadata(provenanceCollections, { configDir: provenanceRoot, base });
|
|
16413
|
+
const places = /* @__PURE__ */ new Map();
|
|
16414
|
+
for (const doc of loaded.values()) {
|
|
16415
|
+
if (!wantsProvenance) break;
|
|
16416
|
+
const place = provenancePlace(
|
|
16417
|
+
doc.label,
|
|
16418
|
+
doc.extracted.data,
|
|
16419
|
+
manifests,
|
|
16420
|
+
declaredCollections,
|
|
16421
|
+
provenanceRoot,
|
|
16422
|
+
base
|
|
16423
|
+
);
|
|
16424
|
+
if (place !== void 0) {
|
|
16425
|
+
places.set(doc.label, place);
|
|
16426
|
+
doc.provenanceManifest = { absPath: place.absPath, entry: place.entry, join: place.join };
|
|
16427
|
+
}
|
|
16428
|
+
const named = rangesByPage.get(doc.label);
|
|
16429
|
+
if (named !== void 0 && generatedBy !== void 0) {
|
|
16430
|
+
doc.attribution = { targets: named, generatedBy };
|
|
16431
|
+
}
|
|
16432
|
+
}
|
|
16433
|
+
const stampOf = (doc) => {
|
|
16434
|
+
if (!places.has(doc.label)) return doc.extracted.data[PROVENANCE_FIELD];
|
|
16435
|
+
const members = memberOf(provenanceCollections, provenanceRoot, base, doc.label);
|
|
16436
|
+
return mergeExternalMetadata(doc.label, doc.extracted, provenanceIndex, members, base).extracted.data[PROVENANCE_FIELD];
|
|
16437
|
+
};
|
|
14919
16438
|
const derived = await deriveMetadata([...loaded.values()], {
|
|
14920
16439
|
cwd,
|
|
14921
16440
|
base,
|
|
@@ -14926,7 +16445,9 @@ async function runDerive(opts) {
|
|
|
14926
16445
|
commands,
|
|
14927
16446
|
cache: opts.cache ?? true,
|
|
14928
16447
|
now: opts.now ?? (() => /* @__PURE__ */ new Date()),
|
|
14929
|
-
reviews: opts.reviews
|
|
16448
|
+
reviews: opts.reviews,
|
|
16449
|
+
machines: machinesOf(config?.derive),
|
|
16450
|
+
...generatedBy !== void 0 ? { generatedBy } : {}
|
|
14930
16451
|
});
|
|
14931
16452
|
assertSourcesAvailable(derived.sources, SOURCE_HINT);
|
|
14932
16453
|
for (const [name2, status] of Object.entries(derived.sources)) {
|
|
@@ -14995,6 +16516,20 @@ async function runDerive(opts) {
|
|
|
14995
16516
|
}
|
|
14996
16517
|
return holder[field];
|
|
14997
16518
|
};
|
|
16519
|
+
const heldManifests = /* @__PURE__ */ new Map();
|
|
16520
|
+
const holdManifest = async (manifest) => {
|
|
16521
|
+
const already = heldManifests.get(manifest.absPath);
|
|
16522
|
+
if (already !== void 0) return already;
|
|
16523
|
+
let before;
|
|
16524
|
+
try {
|
|
16525
|
+
before = await readFile13(manifest.absPath, "utf8");
|
|
16526
|
+
} catch (err) {
|
|
16527
|
+
throw new DocmetaError(`Manifest ${manifest.file} could not be read: ${errorMessage(err)}`);
|
|
16528
|
+
}
|
|
16529
|
+
const held = { path: manifest.absPath, before, text: before };
|
|
16530
|
+
heldManifests.set(manifest.absPath, held);
|
|
16531
|
+
return held;
|
|
16532
|
+
};
|
|
14998
16533
|
const results = [];
|
|
14999
16534
|
for (const label of files) {
|
|
15000
16535
|
const failure = errors.get(label);
|
|
@@ -15014,12 +16549,49 @@ async function runDerive(opts) {
|
|
|
15014
16549
|
const at2 = pointerOf(field);
|
|
15015
16550
|
return marks.marked.some((p) => isAtOrUnder(p, at2));
|
|
15016
16551
|
};
|
|
16552
|
+
const place = places.get(label);
|
|
16553
|
+
const provenance = wantsProvenance ? judgeProvenance(stampOf(doc), derived.provenance?.get(label), record?.fields[PROVENANCE_FIELD] ?? null, place) : void 0;
|
|
15017
16554
|
const compared = fields.map(
|
|
15018
|
-
(field) => compareDerived(field, marks.data[field], record?.fields[field])
|
|
16555
|
+
(field) => field === PROVENANCE_FIELD && provenance !== void 0 ? provenance.field : compareDerived(field, marks.data[field], record?.fields[field])
|
|
15019
16556
|
);
|
|
15020
|
-
const shown = () => compared.map(
|
|
15021
|
-
|
|
16557
|
+
const shown = () => compared.map(
|
|
16558
|
+
(f) => f.field !== PROVENANCE_FIELD && isMarked(f.field) ? redactedDerived(f) : f
|
|
16559
|
+
);
|
|
16560
|
+
const findingsOf = (reported2) => reported2.flatMap(
|
|
16561
|
+
(f) => f.field === PROVENANCE_FIELD && provenance?.judged !== void 0 ? provenanceFindings(provenance.judged.comparisons, provenance.judged.derivation.page.bodyLine) : staleFindings([f], doc.extracted.lineFor)
|
|
16562
|
+
);
|
|
16563
|
+
if (provenance?.judged !== void 0 && flagGeneratedBy !== void 0 && doc.attribution === void 0 && ![...provenance.judged.derivation.evidenceByLine.values()].some((e) => e.rule === 1)) {
|
|
16564
|
+
opts.onNotice?.(
|
|
16565
|
+
`${label}: no uncommitted body lines; --generated-by attributes only what is not yet committed.`
|
|
16566
|
+
);
|
|
16567
|
+
}
|
|
16568
|
+
const stale = (f) => f.status === "stale" || f.status === "unset";
|
|
16569
|
+
const toManifest = provenance !== void 0 && place !== void 0 && stale(provenance.field) ? provenance : void 0;
|
|
16570
|
+
const pending = compared.filter((f) => stale(f) && !(f.field === PROVENANCE_FIELD && toManifest !== void 0));
|
|
16571
|
+
const onPage = pending.find((f) => f.field === PROVENANCE_FIELD);
|
|
16572
|
+
if (onPage !== void 0 && !provenanceFenced(doc.extracted)) {
|
|
16573
|
+
results.push(
|
|
16574
|
+
errorResult2(
|
|
16575
|
+
label,
|
|
16576
|
+
doc.format,
|
|
16577
|
+
`provenance cannot be stamped into the page: in the "${doc.format}" format the metadata is part of the body it pins. Keep provenance in an externalMetadata manifest.`,
|
|
16578
|
+
check,
|
|
16579
|
+
shown()
|
|
16580
|
+
)
|
|
16581
|
+
);
|
|
16582
|
+
continue;
|
|
16583
|
+
}
|
|
15022
16584
|
let changed = false;
|
|
16585
|
+
if (toManifest?.judged !== void 0 && place !== void 0) {
|
|
16586
|
+
const held = await holdManifest(place.manifest);
|
|
16587
|
+
const before = held.text;
|
|
16588
|
+
const where = { entry: place.entry, key: PROVENANCE_FIELD, join: place.join, file: place.manifest.file };
|
|
16589
|
+
held.text = toManifest.judged.plan.length === 0 ? removeManifestKey(held.text, where).text : spliceManifestValue(held.text, { ...where, value: toManifest.judged.plan }).text;
|
|
16590
|
+
if (held.text !== before) {
|
|
16591
|
+
changed = true;
|
|
16592
|
+
if (!dryRun) markWritten(toManifest.field);
|
|
16593
|
+
}
|
|
16594
|
+
}
|
|
15023
16595
|
if (pending.length > 0) {
|
|
15024
16596
|
if (typeof doc.apply !== "function") {
|
|
15025
16597
|
if (!dryRun) {
|
|
@@ -15037,20 +16609,37 @@ async function runDerive(opts) {
|
|
|
15037
16609
|
changed = true;
|
|
15038
16610
|
} else {
|
|
15039
16611
|
const patch = {};
|
|
16612
|
+
const deletions = [];
|
|
15040
16613
|
for (const f of pending) {
|
|
16614
|
+
if (f.field === PROVENANCE_FIELD) {
|
|
16615
|
+
if (Array.isArray(f.derived) && f.derived.length === 0) deletions.push(f.field);
|
|
16616
|
+
else patch[f.field] = f.derived;
|
|
16617
|
+
continue;
|
|
16618
|
+
}
|
|
15041
16619
|
patch[f.field] = isMarked(f.field) ? await sealField(f.field, f.derived, marks.marked) : f.derived;
|
|
15042
16620
|
}
|
|
16621
|
+
const apply = doc.apply;
|
|
16622
|
+
const write = (value) => apply(doc.content, value, {
|
|
16623
|
+
filePath: label,
|
|
16624
|
+
elements: doc.elements,
|
|
16625
|
+
...deletions.length > 0 ? { deletions } : {}
|
|
16626
|
+
});
|
|
15043
16627
|
let next;
|
|
15044
16628
|
try {
|
|
15045
|
-
next =
|
|
16629
|
+
next = write(patch);
|
|
16630
|
+
const plan = patch[PROVENANCE_FIELD];
|
|
16631
|
+
const shift = bodyShift(doc.content, next);
|
|
16632
|
+
if (shift > 0 && Array.isArray(plan)) {
|
|
16633
|
+
next = write({ ...patch, [PROVENANCE_FIELD]: shiftEntries(provenanceEntries(plan), shift) });
|
|
16634
|
+
}
|
|
15046
16635
|
} catch (err) {
|
|
15047
16636
|
results.push(errorResult2(label, doc.format, errorMessage(err), check, shown()));
|
|
15048
16637
|
continue;
|
|
15049
16638
|
}
|
|
15050
|
-
changed = next !== doc.content;
|
|
15051
|
-
if (
|
|
16639
|
+
changed = changed || next !== doc.content;
|
|
16640
|
+
if (next !== doc.content && !dryRun) {
|
|
15052
16641
|
await writeFileAtomic(doc.absPath, next);
|
|
15053
|
-
for (const f of pending) f
|
|
16642
|
+
for (const f of pending) markWritten(f);
|
|
15054
16643
|
}
|
|
15055
16644
|
}
|
|
15056
16645
|
}
|
|
@@ -15060,18 +16649,32 @@ async function runDerive(opts) {
|
|
|
15060
16649
|
format: doc.format,
|
|
15061
16650
|
fields: reported,
|
|
15062
16651
|
changed,
|
|
15063
|
-
...check ? { findings:
|
|
16652
|
+
...check ? { findings: findingsOf(reported) } : {}
|
|
15064
16653
|
});
|
|
15065
16654
|
}
|
|
16655
|
+
if (!dryRun) {
|
|
16656
|
+
for (const held of heldManifests.values()) {
|
|
16657
|
+
if (held.text !== held.before) await writeFileAtomic(held.path, held.text);
|
|
16658
|
+
}
|
|
16659
|
+
}
|
|
16660
|
+
const summary = summarize2(results);
|
|
15066
16661
|
return {
|
|
15067
16662
|
results,
|
|
15068
|
-
summary:
|
|
16663
|
+
summary: wantsProvenance ? { ...summary, ranges: countRanges(results) } : summary,
|
|
15069
16664
|
dryRun,
|
|
15070
16665
|
check,
|
|
15071
16666
|
sources: derived.sources,
|
|
15072
16667
|
frame: { cwd, base: configDir ?? cwd, runBase: base }
|
|
15073
16668
|
};
|
|
15074
16669
|
}
|
|
16670
|
+
function deriveFailed(run2) {
|
|
16671
|
+
if (run2.summary.errors > 0) return true;
|
|
16672
|
+
return run2.check && run2.results.some((r) => (r.findings ?? []).length > 0);
|
|
16673
|
+
}
|
|
16674
|
+
function machineName(value) {
|
|
16675
|
+
const name2 = value?.trim();
|
|
16676
|
+
return name2 === void 0 || name2 === "" ? void 0 : name2;
|
|
16677
|
+
}
|
|
15075
16678
|
function resolveFields(flag, configured, commands) {
|
|
15076
16679
|
const names = flag ?? configured;
|
|
15077
16680
|
if (names === void 0 || names.length === 0) {
|
|
@@ -15139,6 +16742,59 @@ function summarize2(results) {
|
|
|
15139
16742
|
}
|
|
15140
16743
|
return summary;
|
|
15141
16744
|
}
|
|
16745
|
+
function markWritten(field) {
|
|
16746
|
+
field.written = true;
|
|
16747
|
+
for (const range of field.ranges ?? []) {
|
|
16748
|
+
if (range.status !== "current") range.written = true;
|
|
16749
|
+
}
|
|
16750
|
+
}
|
|
16751
|
+
function countRanges(results) {
|
|
16752
|
+
let n = 0;
|
|
16753
|
+
for (const r of results) {
|
|
16754
|
+
for (const f of r.fields) n += (f.ranges ?? []).filter((range) => range.written).length;
|
|
16755
|
+
}
|
|
16756
|
+
return n;
|
|
16757
|
+
}
|
|
16758
|
+
function judgeProvenance(asserted, derivation, value, place) {
|
|
16759
|
+
const base = {
|
|
16760
|
+
field: PROVENANCE_FIELD,
|
|
16761
|
+
...asserted !== void 0 ? { asserted } : {},
|
|
16762
|
+
...place !== void 0 ? { manifest: place.manifest.file } : {}
|
|
16763
|
+
};
|
|
16764
|
+
if (derivation === void 0) {
|
|
16765
|
+
return { field: { ...base, derived: null, status: "unknown", written: false } };
|
|
16766
|
+
}
|
|
16767
|
+
const comparisons = compareProvenance(provenanceEntries(asserted), derivation);
|
|
16768
|
+
const plan = planProvenanceWrite(comparisons, derivation);
|
|
16769
|
+
const settled = comparisons.every((c) => c.status === "current");
|
|
16770
|
+
const status = settled ? "current" : asserted === void 0 || comparisons.every((c) => c.status === "unset") ? "unset" : "stale";
|
|
16771
|
+
return {
|
|
16772
|
+
field: {
|
|
16773
|
+
...base,
|
|
16774
|
+
derived: plan,
|
|
16775
|
+
source: "git",
|
|
16776
|
+
...value !== null ? { evidence: value.evidence } : {},
|
|
16777
|
+
status,
|
|
16778
|
+
written: false,
|
|
16779
|
+
ranges: rangeResults(comparisons, derivation.page.bodyLine)
|
|
16780
|
+
},
|
|
16781
|
+
judged: { derivation, comparisons, plan }
|
|
16782
|
+
};
|
|
16783
|
+
}
|
|
16784
|
+
function bodyShift(before, after) {
|
|
16785
|
+
const was = readProvenancePage(before).body;
|
|
16786
|
+
const now = readProvenancePage(after).body;
|
|
16787
|
+
const shift = now.length - was.length;
|
|
16788
|
+
if (shift <= 0) return 0;
|
|
16789
|
+
return now.slice(shift).join("\n") === was.join("\n") ? shift : 0;
|
|
16790
|
+
}
|
|
16791
|
+
function shiftEntries(entries, shift) {
|
|
16792
|
+
return entries.map((e) => {
|
|
16793
|
+
const span = parseLines(e.lines);
|
|
16794
|
+
if (span === void 0) return e;
|
|
16795
|
+
return { ...e, lines: lineSpec({ start: span.start + shift, end: span.end + shift }) };
|
|
16796
|
+
});
|
|
16797
|
+
}
|
|
15142
16798
|
|
|
15143
16799
|
// src/meta/reporters/fill.ts
|
|
15144
16800
|
var FILL_FORMATS = ["pretty", "json", "github"];
|
|
@@ -15174,6 +16830,26 @@ function renderFillPretty(run2, opts = {}) {
|
|
|
15174
16830
|
` ${c.cyan(f.field)} ${formatValue(f.value)} ${c.dim(score(f.confidence))}`
|
|
15175
16831
|
);
|
|
15176
16832
|
}
|
|
16833
|
+
const record = result.metaProvenance;
|
|
16834
|
+
if (record?.written === true) {
|
|
16835
|
+
lines.push(
|
|
16836
|
+
` ${c.cyan("meta-provenance")} ${record.entry["generated-by"]}: ${record.entry.fields.join(", ")}`
|
|
16837
|
+
);
|
|
16838
|
+
} else if (record?.skipReason === "schema-mismatch") {
|
|
16839
|
+
lines.push(
|
|
16840
|
+
c.dim(" meta-provenance not written: this page's schemas do not allow it")
|
|
16841
|
+
);
|
|
16842
|
+
} else if (record?.skipReason === "manifest-owned") {
|
|
16843
|
+
lines.push(
|
|
16844
|
+
c.dim(
|
|
16845
|
+
` meta-provenance not written: owned by manifest ${record.manifest}, which manni meta fill does not write`
|
|
16846
|
+
)
|
|
16847
|
+
);
|
|
16848
|
+
} else if (record?.skipReason === "unwritable") {
|
|
16849
|
+
lines.push(
|
|
16850
|
+
c.dim(` meta-provenance not written: ${result.format} metadata cannot hold it`)
|
|
16851
|
+
);
|
|
16852
|
+
}
|
|
15177
16853
|
if (low.length > 0) {
|
|
15178
16854
|
lines.push(
|
|
15179
16855
|
c.dim(
|
|
@@ -15325,26 +17001,49 @@ function renderDerivePretty(run2, opts = {}) {
|
|
|
15325
17001
|
lines.push(`${result.file} ${c.dim("current")}${unknownNote(result, c.dim)}`);
|
|
15326
17002
|
continue;
|
|
15327
17003
|
}
|
|
15328
|
-
|
|
15329
|
-
const
|
|
15330
|
-
const changeWidth = Math.max(...pending.map((f) => change(f).length));
|
|
17004
|
+
const page = [];
|
|
17005
|
+
const held = /* @__PURE__ */ new Map();
|
|
15331
17006
|
for (const f of pending) {
|
|
15332
|
-
|
|
15333
|
-
|
|
15334
|
-
|
|
15335
|
-
|
|
15336
|
-
const
|
|
15337
|
-
|
|
17007
|
+
if (f.ranges === void 0) {
|
|
17008
|
+
page.push(fieldRow(f, c));
|
|
17009
|
+
continue;
|
|
17010
|
+
}
|
|
17011
|
+
const rows = f.ranges.filter((r) => r.status !== "current").map((r) => rangeRow(f, r, c));
|
|
17012
|
+
if (f.manifest === void 0) page.push(...rows);
|
|
17013
|
+
else held.set(f.manifest, [...held.get(f.manifest) ?? [], ...rows]);
|
|
17014
|
+
}
|
|
17015
|
+
const all = [...page, ...[...held.values()].flat()];
|
|
17016
|
+
const nameWidth = Math.max(...all.map((r) => r.name.length));
|
|
17017
|
+
const changeWidth = Math.max(...all.map((r) => r.plain.length));
|
|
17018
|
+
const print = (row) => {
|
|
17019
|
+
const pad2 = " ".repeat(changeWidth - row.plain.length);
|
|
17020
|
+
lines.push(` ${c.cyan(row.name.padEnd(nameWidth))} ${row.change}${pad2} ${row.trail}`);
|
|
17021
|
+
};
|
|
17022
|
+
if (page.length > 0 || held.size === 0) lines.push(result.file);
|
|
17023
|
+
page.forEach(print);
|
|
17024
|
+
for (const [manifest, rows] of held) {
|
|
17025
|
+
lines.push(`${manifest} ${c.dim(`(for ${result.file})`)}`);
|
|
17026
|
+
rows.forEach(print);
|
|
15338
17027
|
}
|
|
15339
17028
|
const note = unknownNote(result, c.dim);
|
|
15340
17029
|
if (note !== "") lines.push(` ${note}`);
|
|
15341
17030
|
}
|
|
15342
17031
|
const s = run2.summary;
|
|
15343
17032
|
const parts = [plural2(s.files, "file")];
|
|
17033
|
+
const provenance = run2.results.flatMap((r) => r.fields.filter((f) => f.ranges !== void 0));
|
|
17034
|
+
const provenanceFields = provenance.filter((f) => run2.dryRun ? f.status === "stale" || f.status === "unset" : f.written).length;
|
|
15344
17035
|
if (run2.dryRun) {
|
|
15345
|
-
|
|
17036
|
+
const ranges = provenance.reduce((n, f) => n + (f.ranges ?? []).filter((r) => r.status !== "current").length, 0);
|
|
17037
|
+
const fields = s.stale + s.unset - provenanceFields;
|
|
17038
|
+
parts.push(`${s.changed} would change`);
|
|
17039
|
+
if (fields > 0 || ranges === 0) parts.push(plural2(fields, "field"));
|
|
17040
|
+
if (ranges > 0) parts.push(plural2(ranges, "range"));
|
|
15346
17041
|
} else {
|
|
15347
|
-
|
|
17042
|
+
const ranges = s.ranges ?? 0;
|
|
17043
|
+
const fields = s.written - provenanceFields;
|
|
17044
|
+
parts.push(`${s.changed} changed`);
|
|
17045
|
+
if (fields > 0 || ranges === 0) parts.push(`${plural2(fields, "field")} written`);
|
|
17046
|
+
if (ranges > 0) parts.push(`${plural2(ranges, "range")} written`);
|
|
15348
17047
|
}
|
|
15349
17048
|
if (s.errors > 0) parts.push(plural2(s.errors, "error"));
|
|
15350
17049
|
let footer = parts.join(", ");
|
|
@@ -15353,9 +17052,47 @@ function renderDerivePretty(run2, opts = {}) {
|
|
|
15353
17052
|
lines.push(s.errors > 0 ? c.yellow(footer) : footer);
|
|
15354
17053
|
return lines.join("\n");
|
|
15355
17054
|
}
|
|
15356
|
-
function
|
|
17055
|
+
function fieldRow(f, c) {
|
|
15357
17056
|
const from = f.status === "unset" ? "(unset)" : fmt2(f.asserted);
|
|
15358
|
-
|
|
17057
|
+
const to = fmt2(f.derived);
|
|
17058
|
+
return {
|
|
17059
|
+
name: f.field,
|
|
17060
|
+
change: `${c.dim(from)} \u2192 ${c.green(to)}`,
|
|
17061
|
+
plain: `${from} \u2192 ${to}`,
|
|
17062
|
+
trail: c.dim(`(${f.source ?? "a source"}: ${f.evidence ?? "no evidence"})`)
|
|
17063
|
+
};
|
|
17064
|
+
}
|
|
17065
|
+
function rangeRow(f, r, c) {
|
|
17066
|
+
const head = `lines ${r.lines}: `;
|
|
17067
|
+
const machine = r["generated-by"];
|
|
17068
|
+
let from;
|
|
17069
|
+
let to;
|
|
17070
|
+
switch (r.status) {
|
|
17071
|
+
case "moved":
|
|
17072
|
+
from = "";
|
|
17073
|
+
to = `moved from ${r.from?.lines ?? r.lines}`;
|
|
17074
|
+
break;
|
|
17075
|
+
case "stale":
|
|
17076
|
+
from = r.from?.["generated-by"] ?? "(unset)";
|
|
17077
|
+
to = machine;
|
|
17078
|
+
break;
|
|
17079
|
+
case "changed":
|
|
17080
|
+
from = machine;
|
|
17081
|
+
to = "re-derived";
|
|
17082
|
+
break;
|
|
17083
|
+
case "unset":
|
|
17084
|
+
case "current":
|
|
17085
|
+
from = "(unset)";
|
|
17086
|
+
to = machine;
|
|
17087
|
+
break;
|
|
17088
|
+
}
|
|
17089
|
+
const arrow = from === "" ? "" : `${from} \u2192 `;
|
|
17090
|
+
return {
|
|
17091
|
+
name: f.field,
|
|
17092
|
+
change: `${head}${from === "" ? "" : `${c.dim(from)} \u2192 `}${c.green(to)}`,
|
|
17093
|
+
plain: `${head}${arrow}${to}`,
|
|
17094
|
+
trail: c.dim(`(${f.source ?? "git"}: ${r.evidence})`)
|
|
17095
|
+
};
|
|
15359
17096
|
}
|
|
15360
17097
|
function unknownNote(result, dim) {
|
|
15361
17098
|
const unknown = result.fields.filter((f) => f.status === "unknown").map((f) => f.field);
|
|
@@ -15373,6 +17110,15 @@ function stringifyValue(value) {
|
|
|
15373
17110
|
if (typeof value === "string") return value;
|
|
15374
17111
|
return JSON.stringify(value);
|
|
15375
17112
|
}
|
|
17113
|
+
function valueOf2(r, field, value) {
|
|
17114
|
+
if (field !== PROVENANCE_FIELD || r.provenance === void 0) return stringifyValue(value);
|
|
17115
|
+
if (value === void 0 || value === null) return "(unset)";
|
|
17116
|
+
const entries = provenanceEntries(value);
|
|
17117
|
+
if (!Array.isArray(value) || entries.length !== value.length || entries.length === 0) {
|
|
17118
|
+
return stringifyValue(value);
|
|
17119
|
+
}
|
|
17120
|
+
return renderProvenanceValue(entries, r.provenance.bodyLine);
|
|
17121
|
+
}
|
|
15376
17122
|
function evidenceOf(d) {
|
|
15377
17123
|
const repeated = ` (${stringifyValue(d.value)})`;
|
|
15378
17124
|
return d.evidence.endsWith(repeated) ? d.evidence.slice(0, -repeated.length) : d.evidence;
|
|
@@ -15384,6 +17130,10 @@ function annotation(r, field) {
|
|
|
15384
17130
|
if (origin === "derived") {
|
|
15385
17131
|
return d == null ? "" : ` (derived, ${d.source}: ${evidenceOf(d)})`;
|
|
15386
17132
|
}
|
|
17133
|
+
if (field === PROVENANCE_FIELD && r.provenance !== void 0) {
|
|
17134
|
+
if (r.provenance.current !== false) return " (asserted)";
|
|
17135
|
+
return ` (asserted; ${d?.source ?? "git"} says ${valueOf2(r, field, d?.value)})`;
|
|
17136
|
+
}
|
|
15387
17137
|
if (d == null) return " (asserted)";
|
|
15388
17138
|
if (compareDerived(field, r.resolved?.[field], d).status === "current") {
|
|
15389
17139
|
return " (asserted)";
|
|
@@ -15404,7 +17154,7 @@ function renderGet(results, fields, opts = {}) {
|
|
|
15404
17154
|
const note = annotation(r, f);
|
|
15405
17155
|
const suffix = note === "" ? "" : c.dim(note);
|
|
15406
17156
|
lines.push(
|
|
15407
|
-
`${c.dim(`${r.file}:`)} ${f}=${
|
|
17157
|
+
`${c.dim(`${r.file}:`)} ${f}=${valueOf2(r, f, effective[f])}${suffix}`
|
|
15408
17158
|
);
|
|
15409
17159
|
}
|
|
15410
17160
|
}
|
|
@@ -15518,7 +17268,8 @@ export {
|
|
|
15518
17268
|
ToolError,
|
|
15519
17269
|
errorMessage,
|
|
15520
17270
|
programName,
|
|
15521
|
-
|
|
17271
|
+
fail,
|
|
17272
|
+
runIfMain,
|
|
15522
17273
|
externalMetadataUrlProblem,
|
|
15523
17274
|
parseCollections,
|
|
15524
17275
|
selectCollections,
|
|
@@ -15528,8 +17279,6 @@ export {
|
|
|
15528
17279
|
generateEncryptionKey,
|
|
15529
17280
|
encryptValue,
|
|
15530
17281
|
decryptValue,
|
|
15531
|
-
KEYED_PIN_PREFIX,
|
|
15532
|
-
keyedPin,
|
|
15533
17282
|
findGitRoot,
|
|
15534
17283
|
notice,
|
|
15535
17284
|
findConfigFile,
|
|
@@ -15538,6 +17287,27 @@ export {
|
|
|
15538
17287
|
readFamilyConfigFile,
|
|
15539
17288
|
escapeWorkflowCommandMessage,
|
|
15540
17289
|
escapeWorkflowCommandProperty,
|
|
17290
|
+
INTEGRITY_SHAPE,
|
|
17291
|
+
integrityOf,
|
|
17292
|
+
isIntegrity,
|
|
17293
|
+
LineRangeError,
|
|
17294
|
+
normalizeText,
|
|
17295
|
+
splitLines,
|
|
17296
|
+
sliceLines,
|
|
17297
|
+
hashRange,
|
|
17298
|
+
hashLines,
|
|
17299
|
+
isKeyedPin,
|
|
17300
|
+
pinOfLines,
|
|
17301
|
+
MOVE_WINDOW_LINES,
|
|
17302
|
+
MOVE_BUDGET_BYTES,
|
|
17303
|
+
MAX_RANGE_LINES,
|
|
17304
|
+
findWindows,
|
|
17305
|
+
parseLines,
|
|
17306
|
+
lineSpec,
|
|
17307
|
+
spellLines,
|
|
17308
|
+
toFileLines,
|
|
17309
|
+
toBodyLines,
|
|
17310
|
+
splitPageArgument,
|
|
15541
17311
|
ENCRYPTION_KEY_ENV,
|
|
15542
17312
|
resolveEncryptionKey,
|
|
15543
17313
|
writeEncryptionKey,
|
|
@@ -15551,7 +17321,6 @@ export {
|
|
|
15551
17321
|
extractFrontmatter,
|
|
15552
17322
|
isErrorSeverity,
|
|
15553
17323
|
DocmetaError,
|
|
15554
|
-
deepEqual,
|
|
15555
17324
|
toJsonText,
|
|
15556
17325
|
applyFrontmatter,
|
|
15557
17326
|
extractorForExtension,
|
|
@@ -15568,9 +17337,6 @@ export {
|
|
|
15568
17337
|
DEFAULT_TTL_HOURS,
|
|
15569
17338
|
schemaCacheDir,
|
|
15570
17339
|
SchemaCache,
|
|
15571
|
-
INTEGRITY_SHAPE,
|
|
15572
|
-
integrityOf,
|
|
15573
|
-
isIntegrity,
|
|
15574
17340
|
listBuiltins,
|
|
15575
17341
|
classifyRef,
|
|
15576
17342
|
fetchSchemaBytes,
|
|
@@ -15579,7 +17345,6 @@ export {
|
|
|
15579
17345
|
resolveBaselineRequest,
|
|
15580
17346
|
settleBaseline,
|
|
15581
17347
|
DEFAULT_SCHEMAS,
|
|
15582
|
-
FILE_SCHEMA_KEY,
|
|
15583
17348
|
schemaEntryRef,
|
|
15584
17349
|
collectSchemaPins,
|
|
15585
17350
|
rebaseConfigSchemaRefs,
|
|
@@ -15627,6 +17392,7 @@ export {
|
|
|
15627
17392
|
orphanEntries,
|
|
15628
17393
|
orphanJoins,
|
|
15629
17394
|
orphanError,
|
|
17395
|
+
spliceManifestValue,
|
|
15630
17396
|
isParamName,
|
|
15631
17397
|
memberOf,
|
|
15632
17398
|
Validator,
|
|
@@ -15667,6 +17433,7 @@ export {
|
|
|
15667
17433
|
runQuery,
|
|
15668
17434
|
runFill,
|
|
15669
17435
|
runDerive,
|
|
17436
|
+
deriveFailed,
|
|
15670
17437
|
FILL_FORMATS,
|
|
15671
17438
|
FILL_FORMAT_LIST,
|
|
15672
17439
|
isFillFormat,
|
|
@@ -15681,4 +17448,4 @@ export {
|
|
|
15681
17448
|
renderQuery,
|
|
15682
17449
|
renderQueryCsv
|
|
15683
17450
|
};
|
|
15684
|
-
//# sourceMappingURL=chunk-
|
|
17451
|
+
//# sourceMappingURL=chunk-2DC2GSRH.js.map
|