perttool 0.7.1 → 0.8.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/CHANGELOG.md +33 -1
- package/README.md +94 -18
- package/dist/actuals/lifecycle.d.ts +6 -20
- package/dist/actuals/lifecycle.d.ts.map +1 -1
- package/dist/actuals/lifecycle.js +5 -176
- package/dist/actuals/lifecycle.js.map +1 -1
- package/dist/actuals/reduction.d.ts +20 -0
- package/dist/actuals/reduction.d.ts.map +1 -0
- package/dist/actuals/reduction.js +175 -0
- package/dist/actuals/reduction.js.map +1 -0
- package/dist/analysis/service.d.ts +33 -0
- package/dist/analysis/service.d.ts.map +1 -0
- package/dist/analysis/service.js +153 -0
- package/dist/analysis/service.js.map +1 -0
- package/dist/analysis/target-temporal-input.d.ts +88 -0
- package/dist/analysis/target-temporal-input.d.ts.map +1 -0
- package/dist/analysis/target-temporal-input.js +249 -0
- package/dist/analysis/target-temporal-input.js.map +1 -0
- package/dist/analysis/temporal-deadline.d.ts +1 -1
- package/dist/analysis/temporal-deadline.d.ts.map +1 -1
- package/dist/analysis/temporal-precedence.d.ts +1 -1
- package/dist/analysis/temporal-precedence.d.ts.map +1 -1
- package/dist/analysis/temporal-precedence.js +1 -1
- package/dist/analysis/temporal-precedence.js.map +1 -1
- package/dist/analysis/temporal-resource.d.ts +1 -1
- package/dist/analysis/temporal-resource.d.ts.map +1 -1
- package/dist/analysis/temporal-resource.js +1 -1
- package/dist/analysis/temporal-resource.js.map +1 -1
- package/dist/application/advance-history.d.ts +2 -1
- package/dist/application/advance-history.d.ts.map +1 -1
- package/dist/application/advance-history.js +1 -1
- package/dist/application/advance-history.js.map +1 -1
- package/dist/application/analyze.d.ts +1 -32
- package/dist/application/analyze.d.ts.map +1 -1
- package/dist/application/analyze.js +1 -152
- package/dist/application/analyze.js.map +1 -1
- package/dist/application/check.d.ts +1 -42
- package/dist/application/check.d.ts.map +1 -1
- package/dist/application/check.js +1 -109
- package/dist/application/check.js.map +1 -1
- package/dist/application/cli-facade.d.ts +85 -0
- package/dist/application/cli-facade.d.ts.map +1 -0
- package/dist/application/cli-facade.js +108 -0
- package/dist/application/cli-facade.js.map +1 -0
- package/dist/application/contract6-mutation.js +2 -2
- package/dist/application/contract6-mutation.js.map +1 -1
- package/dist/application/contract7-mermaid.d.ts.map +1 -1
- package/dist/application/contract7-mermaid.js +3 -3
- package/dist/application/contract7-mermaid.js.map +1 -1
- package/dist/application/contract7-source.d.ts +1 -7
- package/dist/application/contract7-source.d.ts.map +1 -1
- package/dist/application/contract7-source.js +4 -12
- package/dist/application/contract7-source.js.map +1 -1
- package/dist/application/editor-dag-focus.d.ts +52 -0
- package/dist/application/editor-dag-focus.d.ts.map +1 -0
- package/dist/application/editor-dag-focus.js +132 -0
- package/dist/application/editor-dag-focus.js.map +1 -0
- package/dist/application/format.js +2 -2
- package/dist/application/format.js.map +1 -1
- package/dist/application/mutate.d.ts +1 -26
- package/dist/application/mutate.d.ts.map +1 -1
- package/dist/application/mutate.js +1 -205
- package/dist/application/mutate.js.map +1 -1
- package/dist/application/next.js +2 -2
- package/dist/application/next.js.map +1 -1
- package/dist/application/target-actuals-advance.js +2 -2
- package/dist/application/target-actuals-advance.js.map +1 -1
- package/dist/application/target-actuals-analysis.js +2 -2
- package/dist/application/target-actuals-analysis.js.map +1 -1
- package/dist/application/target-actuals-mutation.d.ts.map +1 -1
- package/dist/application/target-actuals-mutation.js +6 -5
- package/dist/application/target-actuals-mutation.js.map +1 -1
- package/dist/application/target-assurance-analysis.js +2 -2
- package/dist/application/target-assurance-analysis.js.map +1 -1
- package/dist/application/target-assurance-write.d.ts +2 -1
- package/dist/application/target-assurance-write.d.ts.map +1 -1
- package/dist/application/target-assurance-write.js +36 -12
- package/dist/application/target-assurance-write.js.map +1 -1
- package/dist/application/target-governance-mutation.js +2 -2
- package/dist/application/target-governance-mutation.js.map +1 -1
- package/dist/application/target-historical-graph.d.ts +124 -0
- package/dist/application/target-historical-graph.d.ts.map +1 -0
- package/dist/application/target-historical-graph.js +547 -0
- package/dist/application/target-historical-graph.js.map +1 -0
- package/dist/application/target-mutate.d.ts +1 -17
- package/dist/application/target-mutate.d.ts.map +1 -1
- package/dist/application/target-mutate.js +1 -159
- package/dist/application/target-mutate.js.map +1 -1
- package/dist/application/target-project-history.d.ts +2 -1
- package/dist/application/target-project-history.d.ts.map +1 -1
- package/dist/application/target-project-history.js +3 -3
- package/dist/application/target-project-history.js.map +1 -1
- package/dist/application/target-temporal-analysis.js +2 -2
- package/dist/application/target-temporal-analysis.js.map +1 -1
- package/dist/application/target-temporal-input.d.ts +1 -87
- package/dist/application/target-temporal-input.d.ts.map +1 -1
- package/dist/application/target-temporal-input.js +1 -248
- package/dist/application/target-temporal-input.js.map +1 -1
- package/dist/application/target-unit-migration-candidate.js +2 -2
- package/dist/application/target-unit-migration-candidate.js.map +1 -1
- package/dist/assurance/advance.js +2 -2
- package/dist/assurance/advance.js.map +1 -1
- package/dist/assurance/canonical.js +2 -2
- package/dist/assurance/canonical.js.map +1 -1
- package/dist/assurance/compatibility.js +2 -2
- package/dist/assurance/compatibility.js.map +1 -1
- package/dist/assurance/mermaid.d.ts +4 -2
- package/dist/assurance/mermaid.d.ts.map +1 -1
- package/dist/assurance/mermaid.js +11 -7
- package/dist/assurance/mermaid.js.map +1 -1
- package/dist/assurance/mutation.d.ts.map +1 -1
- package/dist/assurance/mutation.js +9 -23
- package/dist/assurance/mutation.js.map +1 -1
- package/dist/cli.js +66 -26
- package/dist/cli.js.map +1 -1
- package/dist/command/assurance-discovery.d.ts.map +1 -1
- package/dist/command/assurance-discovery.js +62 -2
- package/dist/command/assurance-discovery.js.map +1 -1
- package/dist/command/assurance-usage.d.ts.map +1 -1
- package/dist/command/assurance-usage.js +11 -0
- package/dist/command/assurance-usage.js.map +1 -1
- package/dist/conversion/mermaid-import.js +3 -3
- package/dist/conversion/mermaid-import.js.map +1 -1
- package/dist/conversion/mermaid.d.ts +1 -1
- package/dist/conversion/mermaid.d.ts.map +1 -1
- package/dist/conversion/mermaid.js +4 -4
- package/dist/conversion/mermaid.js.map +1 -1
- package/dist/core/index.d.ts +33 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/source.d.ts +8 -0
- package/dist/core/source.d.ts.map +1 -0
- package/dist/core/source.js +11 -0
- package/dist/core/source.js.map +1 -0
- package/dist/formatter/source-formatter.d.ts +1 -1
- package/dist/formatter/source-formatter.d.ts.map +1 -1
- package/dist/formatter/source-formatter.js +1 -1
- package/dist/formatter/source-formatter.js.map +1 -1
- package/dist/guidance/validator.js +2 -2
- package/dist/guidance/validator.js.map +1 -1
- package/dist/help/assurance-guide.d.ts.map +1 -1
- package/dist/help/assurance-guide.js +78 -0
- package/dist/help/assurance-guide.js.map +1 -1
- package/dist/history/git-probe.d.ts +71 -0
- package/dist/history/git-probe.d.ts.map +1 -1
- package/dist/history/git-probe.js +517 -0
- package/dist/history/git-probe.js.map +1 -1
- package/dist/history/historical-graph.d.ts +174 -0
- package/dist/history/historical-graph.d.ts.map +1 -0
- package/dist/history/historical-graph.js +870 -0
- package/dist/history/historical-graph.js.map +1 -0
- package/dist/history/historical-transition.d.ts +311 -0
- package/dist/history/historical-transition.d.ts.map +1 -0
- package/dist/history/historical-transition.js +998 -0
- package/dist/history/historical-transition.js.map +1 -0
- package/dist/history/project-history.d.ts.map +1 -1
- package/dist/history/project-history.js +2 -2
- package/dist/history/project-history.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/io/document-file.js +2 -2
- package/dist/io/document-file.js.map +1 -1
- package/dist/io/safe-write.js +1 -1
- package/dist/io/safe-write.js.map +1 -1
- package/dist/model/sha256.d.ts +5 -0
- package/dist/model/sha256.d.ts.map +1 -0
- package/dist/model/sha256.js +105 -0
- package/dist/model/sha256.js.map +1 -0
- package/dist/mutation/advance.js +3 -3
- package/dist/mutation/advance.js.map +1 -1
- package/dist/mutation/diagnostics.d.ts +2 -2
- package/dist/mutation/diagnostics.d.ts.map +1 -1
- package/dist/mutation/diagnostics.js.map +1 -1
- package/dist/mutation/planner.d.ts +27 -0
- package/dist/mutation/planner.d.ts.map +1 -0
- package/dist/mutation/planner.js +206 -0
- package/dist/mutation/planner.js.map +1 -0
- package/dist/mutation/source.d.ts +2 -1
- package/dist/mutation/source.d.ts.map +1 -1
- package/dist/mutation/source.js +14 -0
- package/dist/mutation/source.js.map +1 -1
- package/dist/mutation/target-planner.d.ts +18 -0
- package/dist/mutation/target-planner.d.ts.map +1 -0
- package/dist/mutation/target-planner.js +160 -0
- package/dist/mutation/target-planner.js.map +1 -0
- package/dist/mutation/task.d.ts.map +1 -1
- package/dist/mutation/task.js +19 -11
- package/dist/mutation/task.js.map +1 -1
- package/dist/mutation/types.d.ts +1 -1
- package/dist/mutation/types.d.ts.map +1 -1
- package/dist/node/historical-host.d.ts +6 -0
- package/dist/node/historical-host.d.ts.map +1 -0
- package/dist/node/historical-host.js +8 -0
- package/dist/node/historical-host.js.map +1 -0
- package/dist/node/host.d.ts +3 -0
- package/dist/node/host.d.ts.map +1 -0
- package/dist/node/host.js +48 -0
- package/dist/node/host.js.map +1 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +2 -0
- package/dist/node/index.js.map +1 -0
- package/dist/perttool-vscode-private-0.0.0-d1578b9.vsix +0 -0
- package/dist/ports/node-host.d.ts +41 -0
- package/dist/ports/node-host.d.ts.map +1 -0
- package/dist/ports/node-host.js +2 -0
- package/dist/ports/node-host.js.map +1 -0
- package/dist/recommendation/override.d.ts +40 -2
- package/dist/recommendation/override.d.ts.map +1 -1
- package/dist/recommendation/override.js +5 -6
- package/dist/recommendation/override.js.map +1 -1
- package/dist/schema/registry.d.ts.map +1 -1
- package/dist/schema/registry.js +1 -0
- package/dist/schema/registry.js.map +1 -1
- package/dist/semantic/check.d.ts +43 -0
- package/dist/semantic/check.d.ts.map +1 -0
- package/dist/semantic/check.js +110 -0
- package/dist/semantic/check.js.map +1 -0
- package/dist/session/document-session.d.ts +109 -0
- package/dist/session/document-session.d.ts.map +1 -0
- package/dist/session/document-session.js +432 -0
- package/dist/session/document-session.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +18 -4
- package/schemas/Perttool.HistoricalGraphResult.v1.schema.json +654 -0
|
@@ -5,6 +5,12 @@ import { dirname, isAbsolute, relative, resolve, sep, } from "node:path";
|
|
|
5
5
|
import { digestDocumentBytes } from "../io/document-file.js";
|
|
6
6
|
export const GIT_HISTORY_PROBE_MODEL_VERSION = 1;
|
|
7
7
|
export const ADVANCE_HISTORY_BASELINE_MODEL_VERSION = 1;
|
|
8
|
+
export const HISTORICAL_GIT_EVIDENCE_MODEL_VERSION = 1;
|
|
9
|
+
export const HISTORICAL_GIT_EVIDENCE_LIMITS = Object.freeze({
|
|
10
|
+
inspectedCommits: 2_048,
|
|
11
|
+
rawBytesPerSnapshot: 8_388_608,
|
|
12
|
+
aggregateRawSnapshotBytes: 134_217_728,
|
|
13
|
+
});
|
|
8
14
|
const gitTimeoutMilliseconds = 30_000;
|
|
9
15
|
const gitMaxBufferBytes = 64 * 1024 * 1024;
|
|
10
16
|
const availabilityOrder = new Map([
|
|
@@ -574,6 +580,517 @@ export async function probeGitHistory(request, dependencies = {}) {
|
|
|
574
580
|
availability: allCauses,
|
|
575
581
|
};
|
|
576
582
|
}
|
|
583
|
+
function historicalLimits(overrides) {
|
|
584
|
+
function select(name) {
|
|
585
|
+
const value = overrides?.[name] ?? HISTORICAL_GIT_EVIDENCE_LIMITS[name];
|
|
586
|
+
return Number.isSafeInteger(value) && value > 0
|
|
587
|
+
? value
|
|
588
|
+
: HISTORICAL_GIT_EVIDENCE_LIMITS[name];
|
|
589
|
+
}
|
|
590
|
+
return Object.freeze({
|
|
591
|
+
inspectedCommits: select("inspectedCommits"),
|
|
592
|
+
rawBytesPerSnapshot: select("rawBytesPerSnapshot"),
|
|
593
|
+
aggregateRawSnapshotBytes: select("aggregateRawSnapshotBytes"),
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
function historicalCause(cause, subject, options = {}) {
|
|
597
|
+
return Object.freeze({
|
|
598
|
+
cause,
|
|
599
|
+
subject,
|
|
600
|
+
commitId: options.commitId ?? null,
|
|
601
|
+
limit: options.limit ?? null,
|
|
602
|
+
actual: options.actual ?? null,
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
function historicalEvidenceResult(request, limits, status, causes, fields = {}) {
|
|
606
|
+
return Object.freeze({
|
|
607
|
+
ok: true,
|
|
608
|
+
modelVersion: HISTORICAL_GIT_EVIDENCE_MODEL_VERSION,
|
|
609
|
+
status,
|
|
610
|
+
ancestryProfile: "first_parent",
|
|
611
|
+
objectFormat: fields.objectFormat ?? null,
|
|
612
|
+
repositoryId: fields.repositoryId ?? null,
|
|
613
|
+
repositoryReadSnapshotId: fields.repositoryReadSnapshotId ?? null,
|
|
614
|
+
repositoryRelativePath: fields.repositoryRelativePath ?? null,
|
|
615
|
+
requestedEndpoint: request.requestedEndpoint ?? "HEAD",
|
|
616
|
+
resolvedEndpoint: fields.resolvedEndpoint ?? null,
|
|
617
|
+
requestedLowerBoundary: request.lowerBoundary ?? null,
|
|
618
|
+
resolvedLowerBoundary: fields.resolvedLowerBoundary ?? null,
|
|
619
|
+
oldestInspectedCommitId: fields.oldestInspectedCommitId ?? null,
|
|
620
|
+
currentSourceDigest: fields.currentSourceDigest ?? null,
|
|
621
|
+
aggregateRawSnapshotBytes: fields.aggregateRawSnapshotBytes ?? 0,
|
|
622
|
+
limits,
|
|
623
|
+
inspectedCommitIds: Object.freeze([
|
|
624
|
+
...(fields.inspectedCommitIds ?? []),
|
|
625
|
+
]),
|
|
626
|
+
snapshots: Object.freeze([...(fields.snapshots ?? [])]),
|
|
627
|
+
causes: Object.freeze([...causes]),
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
function parseHistoricalCommitList(result, operation, objectFormat) {
|
|
631
|
+
const text = stdoutText(result, operation);
|
|
632
|
+
if (typeof text !== "string")
|
|
633
|
+
return text;
|
|
634
|
+
const values = text === "" ? [] : text.split("\n");
|
|
635
|
+
if (values.some((value) => !commitId(value, objectFormat)) ||
|
|
636
|
+
new Set(values).size !== values.length) {
|
|
637
|
+
return malformed(operation);
|
|
638
|
+
}
|
|
639
|
+
return values;
|
|
640
|
+
}
|
|
641
|
+
function resolveHistoricalRevision(executable, repositoryRoot, spelling, objectFormat, operation) {
|
|
642
|
+
const commitCommand = runGit(executable, repositoryRoot, operation, [
|
|
643
|
+
"rev-parse",
|
|
644
|
+
"--verify",
|
|
645
|
+
"--end-of-options",
|
|
646
|
+
`${spelling}^{commit}`,
|
|
647
|
+
]);
|
|
648
|
+
if (commitCommand.ok) {
|
|
649
|
+
const text = stdoutText(commitCommand, operation);
|
|
650
|
+
if (typeof text !== "string")
|
|
651
|
+
return { ok: false, failure: text };
|
|
652
|
+
if (!commitId(text, objectFormat)) {
|
|
653
|
+
return { ok: false, failure: malformed(operation) };
|
|
654
|
+
}
|
|
655
|
+
return { ok: true, commitId: text };
|
|
656
|
+
}
|
|
657
|
+
if (commitCommand.kind === "failure") {
|
|
658
|
+
return { ok: false, failure: commitCommand.failure };
|
|
659
|
+
}
|
|
660
|
+
if (/^[0-9a-fA-F]{4,64}$/.test(spelling)) {
|
|
661
|
+
const disambiguate = runGit(executable, repositoryRoot, `${operation}_disambiguate`, ["rev-parse", `--disambiguate=${spelling.toLowerCase()}`]);
|
|
662
|
+
if (!disambiguate.ok) {
|
|
663
|
+
if (disambiguate.kind === "failure") {
|
|
664
|
+
return { ok: false, failure: disambiguate.failure };
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
const candidates = parseHistoricalCommitList(disambiguate, `${operation}_disambiguate`, objectFormat);
|
|
669
|
+
if (!Array.isArray(candidates)) {
|
|
670
|
+
return { ok: false, failure: candidates };
|
|
671
|
+
}
|
|
672
|
+
if (candidates.length > 1) {
|
|
673
|
+
return { ok: false, cause: "ambiguous_revision" };
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const objectCommand = runGit(executable, repositoryRoot, `${operation}_object`, [
|
|
678
|
+
"rev-parse",
|
|
679
|
+
"--verify",
|
|
680
|
+
"--end-of-options",
|
|
681
|
+
`${spelling}^{object}`,
|
|
682
|
+
]);
|
|
683
|
+
if (!objectCommand.ok) {
|
|
684
|
+
return objectCommand.kind === "failure"
|
|
685
|
+
? { ok: false, failure: objectCommand.failure }
|
|
686
|
+
: { ok: false, cause: "unknown_revision" };
|
|
687
|
+
}
|
|
688
|
+
const objectText = stdoutText(objectCommand, `${operation}_object`);
|
|
689
|
+
if (typeof objectText !== "string") {
|
|
690
|
+
return { ok: false, failure: objectText };
|
|
691
|
+
}
|
|
692
|
+
if (!commitId(objectText, objectFormat)) {
|
|
693
|
+
return { ok: false, failure: malformed(`${operation}_object`) };
|
|
694
|
+
}
|
|
695
|
+
return { ok: false, cause: "non_commit_revision" };
|
|
696
|
+
}
|
|
697
|
+
function readHistoricalTreeEntry(executable, repositoryRoot, commit, relativePath, objectFormat, operation) {
|
|
698
|
+
const command = runGit(executable, repositoryRoot, operation, ["ls-tree", "-z", commit, "--", relativePath]);
|
|
699
|
+
if (!command.ok) {
|
|
700
|
+
return command.kind === "failure"
|
|
701
|
+
? command.failure
|
|
702
|
+
: commandFailure(operation);
|
|
703
|
+
}
|
|
704
|
+
if (command.stdout.length === 0)
|
|
705
|
+
return { kind: "missing" };
|
|
706
|
+
const text = stdoutText(command, operation);
|
|
707
|
+
if (typeof text !== "string")
|
|
708
|
+
return text;
|
|
709
|
+
const match = /^([0-7]{6}) ([a-z]+) ([0-9a-f]+)\t([^\u0000]+)\u0000?$/.exec(text);
|
|
710
|
+
if (match === null ||
|
|
711
|
+
match[4] !== relativePath ||
|
|
712
|
+
!commitId(match[3], objectFormat)) {
|
|
713
|
+
return malformed(operation);
|
|
714
|
+
}
|
|
715
|
+
return Object.freeze({
|
|
716
|
+
kind: "object",
|
|
717
|
+
mode: match[1],
|
|
718
|
+
objectType: match[2],
|
|
719
|
+
objectId: match[3],
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
function regularHistoricalBlob(entry) {
|
|
723
|
+
return entry.kind === "object" &&
|
|
724
|
+
entry.objectType === "blob" &&
|
|
725
|
+
(entry.mode === "100644" || entry.mode === "100755");
|
|
726
|
+
}
|
|
727
|
+
function repositoryIdentity(commonDirectory) {
|
|
728
|
+
const digest = digestDocumentBytes(Buffer.from(commonDirectory, "utf8"));
|
|
729
|
+
return `git-repository:${digest}`;
|
|
730
|
+
}
|
|
731
|
+
export async function probeHistoricalGitEvidence(request, dependencies = {}) {
|
|
732
|
+
const limits = historicalLimits(dependencies.limits);
|
|
733
|
+
const requestedEndpoint = request.requestedEndpoint ?? "HEAD";
|
|
734
|
+
const executable = dependencies.gitExecutable ?? "git";
|
|
735
|
+
const targetPath = resolve(request.targetPath);
|
|
736
|
+
const initialTarget = await captureTarget(targetPath);
|
|
737
|
+
if (!initialTarget.ok) {
|
|
738
|
+
if ("ambiguous" in initialTarget) {
|
|
739
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("ambiguous_path", "target_path")]);
|
|
740
|
+
}
|
|
741
|
+
return initialTarget.failure;
|
|
742
|
+
}
|
|
743
|
+
const repositoryCommand = runGit(executable, dirname(initialTarget.capture.realPath), "historical_repository_root", ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
744
|
+
if (!repositoryCommand.ok) {
|
|
745
|
+
if (repositoryCommand.kind === "failure") {
|
|
746
|
+
return repositoryCommand.failure;
|
|
747
|
+
}
|
|
748
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("no_repository", "repository")], { currentSourceDigest: initialTarget.capture.digest });
|
|
749
|
+
}
|
|
750
|
+
const repositoryText = stdoutText(repositoryCommand, "historical_repository_root");
|
|
751
|
+
if (typeof repositoryText !== "string")
|
|
752
|
+
return repositoryText;
|
|
753
|
+
let repositoryRoot;
|
|
754
|
+
try {
|
|
755
|
+
repositoryRoot = await realpath(repositoryText);
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
return commandFailure("historical_repository_root");
|
|
759
|
+
}
|
|
760
|
+
const relativePath = normalizeRepositoryPath(repositoryRoot, initialTarget.capture.realPath);
|
|
761
|
+
if (relativePath === null) {
|
|
762
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("ambiguous_path", "target_path")], { currentSourceDigest: initialTarget.capture.digest });
|
|
763
|
+
}
|
|
764
|
+
const headCommand = runGit(executable, repositoryRoot, "historical_resolve_head", ["rev-parse", "--verify", "--end-of-options", "HEAD^{commit}"]);
|
|
765
|
+
if (!headCommand.ok) {
|
|
766
|
+
if (headCommand.kind === "failure")
|
|
767
|
+
return headCommand.failure;
|
|
768
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("no_head", "repository")], {
|
|
769
|
+
repositoryRelativePath: relativePath,
|
|
770
|
+
currentSourceDigest: initialTarget.capture.digest,
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
const formatCommand = runGit(executable, repositoryRoot, "historical_object_format", ["rev-parse", "--show-object-format=storage"]);
|
|
774
|
+
if (!formatCommand.ok) {
|
|
775
|
+
return formatCommand.kind === "failure"
|
|
776
|
+
? formatCommand.failure
|
|
777
|
+
: commandFailure("historical_object_format");
|
|
778
|
+
}
|
|
779
|
+
const formatText = stdoutText(formatCommand, "historical_object_format");
|
|
780
|
+
if (typeof formatText !== "string")
|
|
781
|
+
return formatText;
|
|
782
|
+
if (formatText !== "sha1" && formatText !== "sha256") {
|
|
783
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("unsupported_object_format", "repository")], {
|
|
784
|
+
repositoryRelativePath: relativePath,
|
|
785
|
+
currentSourceDigest: initialTarget.capture.digest,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
const objectFormat = formatText;
|
|
789
|
+
const commonCommand = runGit(executable, repositoryRoot, "historical_common_directory", ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
790
|
+
if (!commonCommand.ok) {
|
|
791
|
+
return commonCommand.kind === "failure"
|
|
792
|
+
? commonCommand.failure
|
|
793
|
+
: commandFailure("historical_common_directory");
|
|
794
|
+
}
|
|
795
|
+
const commonText = stdoutText(commonCommand, "historical_common_directory");
|
|
796
|
+
if (typeof commonText !== "string")
|
|
797
|
+
return commonText;
|
|
798
|
+
let commonDirectory;
|
|
799
|
+
try {
|
|
800
|
+
commonDirectory = await realpath(commonText);
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
return commandFailure("historical_common_directory");
|
|
804
|
+
}
|
|
805
|
+
const repositoryId = repositoryIdentity(commonDirectory);
|
|
806
|
+
const baseFields = {
|
|
807
|
+
objectFormat,
|
|
808
|
+
repositoryId,
|
|
809
|
+
repositoryRelativePath: relativePath,
|
|
810
|
+
currentSourceDigest: initialTarget.capture.digest,
|
|
811
|
+
};
|
|
812
|
+
if (request.expectedSourceDigest !== undefined &&
|
|
813
|
+
request.expectedSourceDigest !== initialTarget.capture.digest) {
|
|
814
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("target_changed", "target_path")], baseFields);
|
|
815
|
+
}
|
|
816
|
+
const endpoint = resolveHistoricalRevision(executable, repositoryRoot, requestedEndpoint, objectFormat, "historical_resolve_endpoint");
|
|
817
|
+
if (!endpoint.ok) {
|
|
818
|
+
if ("failure" in endpoint)
|
|
819
|
+
return endpoint.failure;
|
|
820
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause(endpoint.cause, "endpoint")], baseFields);
|
|
821
|
+
}
|
|
822
|
+
const resolvedEndpoint = endpoint.commitId;
|
|
823
|
+
const endpointFields = {
|
|
824
|
+
...baseFields,
|
|
825
|
+
resolvedEndpoint,
|
|
826
|
+
};
|
|
827
|
+
const entryCache = new Map();
|
|
828
|
+
const endpointEntry = readHistoricalTreeEntry(executable, repositoryRoot, resolvedEndpoint, relativePath, objectFormat, "historical_endpoint_tree");
|
|
829
|
+
if ("ok" in endpointEntry)
|
|
830
|
+
return endpointEntry;
|
|
831
|
+
entryCache.set(resolvedEndpoint, endpointEntry);
|
|
832
|
+
if (!regularHistoricalBlob(endpointEntry)) {
|
|
833
|
+
return historicalEvidenceResult(request, limits, "unavailable", [
|
|
834
|
+
historicalCause("endpoint_path_missing", "endpoint", {
|
|
835
|
+
commitId: resolvedEndpoint,
|
|
836
|
+
}),
|
|
837
|
+
], endpointFields);
|
|
838
|
+
}
|
|
839
|
+
const shallowCommand = runGit(executable, repositoryRoot, "historical_shallow_state", ["rev-parse", "--is-shallow-repository"]);
|
|
840
|
+
if (!shallowCommand.ok) {
|
|
841
|
+
return shallowCommand.kind === "failure"
|
|
842
|
+
? shallowCommand.failure
|
|
843
|
+
: commandFailure("historical_shallow_state");
|
|
844
|
+
}
|
|
845
|
+
const shallowText = stdoutText(shallowCommand, "historical_shallow_state");
|
|
846
|
+
if (typeof shallowText !== "string")
|
|
847
|
+
return shallowText;
|
|
848
|
+
if (shallowText !== "true" && shallowText !== "false") {
|
|
849
|
+
return malformed("historical_shallow_state");
|
|
850
|
+
}
|
|
851
|
+
const shallow = shallowText === "true";
|
|
852
|
+
const laneCommand = runGit(executable, repositoryRoot, "historical_first_parent_lane", ["rev-list", "--first-parent", "--reverse", resolvedEndpoint]);
|
|
853
|
+
if (!laneCommand.ok) {
|
|
854
|
+
return laneCommand.kind === "failure"
|
|
855
|
+
? laneCommand.failure
|
|
856
|
+
: commandFailure("historical_first_parent_lane");
|
|
857
|
+
}
|
|
858
|
+
const lane = parseHistoricalCommitList(laneCommand, "historical_first_parent_lane", objectFormat);
|
|
859
|
+
if (!Array.isArray(lane))
|
|
860
|
+
return lane;
|
|
861
|
+
if (lane.length === 0 || lane.at(-1) !== resolvedEndpoint) {
|
|
862
|
+
return malformed("historical_first_parent_lane");
|
|
863
|
+
}
|
|
864
|
+
const laneOrder = new Map(lane.map((id, index) => [id, index]));
|
|
865
|
+
let resolvedLowerBoundary = null;
|
|
866
|
+
let lowerIndex = 0;
|
|
867
|
+
if (request.lowerBoundary !== undefined) {
|
|
868
|
+
const lower = resolveHistoricalRevision(executable, repositoryRoot, request.lowerBoundary, objectFormat, "historical_resolve_lower");
|
|
869
|
+
if (!lower.ok) {
|
|
870
|
+
if ("failure" in lower)
|
|
871
|
+
return lower.failure;
|
|
872
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause(lower.cause, "lower_boundary")], endpointFields);
|
|
873
|
+
}
|
|
874
|
+
resolvedLowerBoundary = lower.commitId;
|
|
875
|
+
const index = laneOrder.get(resolvedLowerBoundary);
|
|
876
|
+
if (index === undefined) {
|
|
877
|
+
return historicalEvidenceResult(request, limits, "unavailable", [
|
|
878
|
+
historicalCause(shallow
|
|
879
|
+
? "shallow_origin"
|
|
880
|
+
: "lower_not_first_parent_ancestor", "lower_boundary", { commitId: resolvedLowerBoundary }),
|
|
881
|
+
], { ...endpointFields, resolvedLowerBoundary });
|
|
882
|
+
}
|
|
883
|
+
lowerIndex = index;
|
|
884
|
+
const lowerEntry = resolvedLowerBoundary === resolvedEndpoint
|
|
885
|
+
? endpointEntry
|
|
886
|
+
: readHistoricalTreeEntry(executable, repositoryRoot, resolvedLowerBoundary, relativePath, objectFormat, "historical_lower_tree");
|
|
887
|
+
if ("ok" in lowerEntry)
|
|
888
|
+
return lowerEntry;
|
|
889
|
+
entryCache.set(resolvedLowerBoundary, lowerEntry);
|
|
890
|
+
if (!regularHistoricalBlob(lowerEntry)) {
|
|
891
|
+
return historicalEvidenceResult(request, limits, "unavailable", [
|
|
892
|
+
historicalCause("lower_path_missing", "lower_boundary", {
|
|
893
|
+
commitId: resolvedLowerBoundary,
|
|
894
|
+
}),
|
|
895
|
+
], { ...endpointFields, resolvedLowerBoundary });
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
const changesCommand = runGit(executable, repositoryRoot, "historical_path_changes", [
|
|
899
|
+
"rev-list",
|
|
900
|
+
"--first-parent",
|
|
901
|
+
"--full-history",
|
|
902
|
+
"--reverse",
|
|
903
|
+
resolvedEndpoint,
|
|
904
|
+
"--",
|
|
905
|
+
relativePath,
|
|
906
|
+
]);
|
|
907
|
+
if (!changesCommand.ok) {
|
|
908
|
+
return changesCommand.kind === "failure"
|
|
909
|
+
? changesCommand.failure
|
|
910
|
+
: commandFailure("historical_path_changes");
|
|
911
|
+
}
|
|
912
|
+
const pathChanges = parseHistoricalCommitList(changesCommand, "historical_path_changes", objectFormat);
|
|
913
|
+
if (!Array.isArray(pathChanges))
|
|
914
|
+
return pathChanges;
|
|
915
|
+
if (pathChanges.some((id) => !laneOrder.has(id))) {
|
|
916
|
+
return malformed("historical_path_changes");
|
|
917
|
+
}
|
|
918
|
+
const selected = new Set();
|
|
919
|
+
if (resolvedLowerBoundary !== null)
|
|
920
|
+
selected.add(resolvedLowerBoundary);
|
|
921
|
+
for (const id of pathChanges) {
|
|
922
|
+
const index = laneOrder.get(id);
|
|
923
|
+
if (index >= lowerIndex)
|
|
924
|
+
selected.add(id);
|
|
925
|
+
}
|
|
926
|
+
selected.add(resolvedEndpoint);
|
|
927
|
+
const inspectedCommitIds = [...selected].sort((left, right) => laneOrder.get(left) - laneOrder.get(right));
|
|
928
|
+
if (inspectedCommitIds.length > limits.inspectedCommits) {
|
|
929
|
+
return historicalEvidenceResult(request, limits, "unavailable", [
|
|
930
|
+
historicalCause("hard_limit", "inspection", {
|
|
931
|
+
limit: "inspectedCommits",
|
|
932
|
+
actual: inspectedCommitIds.length,
|
|
933
|
+
}),
|
|
934
|
+
], { ...endpointFields, resolvedLowerBoundary });
|
|
935
|
+
}
|
|
936
|
+
const pending = [];
|
|
937
|
+
let aggregateRawSnapshotBytes = 0;
|
|
938
|
+
for (const inspectedCommitId of inspectedCommitIds) {
|
|
939
|
+
const metadataCommand = runGit(executable, repositoryRoot, "historical_commit_metadata", ["show", "-s", "--format=%P%x00%cI", inspectedCommitId]);
|
|
940
|
+
if (!metadataCommand.ok) {
|
|
941
|
+
return metadataCommand.kind === "failure"
|
|
942
|
+
? metadataCommand.failure
|
|
943
|
+
: historicalEvidenceResult(request, limits, "unavailable", [
|
|
944
|
+
historicalCause("object_read_failed", "inspection", {
|
|
945
|
+
commitId: inspectedCommitId,
|
|
946
|
+
}),
|
|
947
|
+
], { ...endpointFields, resolvedLowerBoundary });
|
|
948
|
+
}
|
|
949
|
+
const metadataText = stdoutText(metadataCommand, "historical_commit_metadata");
|
|
950
|
+
if (typeof metadataText !== "string")
|
|
951
|
+
return metadataText;
|
|
952
|
+
const metadata = parseGitCommitMetadata(metadataText, objectFormat);
|
|
953
|
+
if (metadata === null)
|
|
954
|
+
return malformed("historical_commit_metadata");
|
|
955
|
+
let entry = entryCache.get(inspectedCommitId);
|
|
956
|
+
if (entry === undefined) {
|
|
957
|
+
const read = readHistoricalTreeEntry(executable, repositoryRoot, inspectedCommitId, relativePath, objectFormat, "historical_snapshot_tree");
|
|
958
|
+
if ("ok" in read)
|
|
959
|
+
return read;
|
|
960
|
+
entry = read;
|
|
961
|
+
entryCache.set(inspectedCommitId, entry);
|
|
962
|
+
}
|
|
963
|
+
let blobId = null;
|
|
964
|
+
let source = null;
|
|
965
|
+
let sourceDigest = null;
|
|
966
|
+
if (entry.kind === "object" && entry.objectType === "blob") {
|
|
967
|
+
blobId = entry.objectId;
|
|
968
|
+
const sourceCommand = runGit(executable, repositoryRoot, "historical_snapshot_blob", ["cat-file", "blob", blobId]);
|
|
969
|
+
if (!sourceCommand.ok) {
|
|
970
|
+
return sourceCommand.kind === "failure"
|
|
971
|
+
? sourceCommand.failure
|
|
972
|
+
: historicalEvidenceResult(request, limits, "unavailable", [
|
|
973
|
+
historicalCause("object_read_failed", "inspection", {
|
|
974
|
+
commitId: inspectedCommitId,
|
|
975
|
+
}),
|
|
976
|
+
], { ...endpointFields, resolvedLowerBoundary });
|
|
977
|
+
}
|
|
978
|
+
if (sourceCommand.stdout.length > limits.rawBytesPerSnapshot) {
|
|
979
|
+
return historicalEvidenceResult(request, limits, "unavailable", [
|
|
980
|
+
historicalCause("hard_limit", "inspection", {
|
|
981
|
+
commitId: inspectedCommitId,
|
|
982
|
+
limit: "rawBytesPerSnapshot",
|
|
983
|
+
actual: sourceCommand.stdout.length,
|
|
984
|
+
}),
|
|
985
|
+
], { ...endpointFields, resolvedLowerBoundary });
|
|
986
|
+
}
|
|
987
|
+
const nextAggregate = aggregateRawSnapshotBytes + sourceCommand.stdout.length;
|
|
988
|
+
if (nextAggregate > limits.aggregateRawSnapshotBytes) {
|
|
989
|
+
return historicalEvidenceResult(request, limits, "unavailable", [
|
|
990
|
+
historicalCause("hard_limit", "inspection", {
|
|
991
|
+
commitId: inspectedCommitId,
|
|
992
|
+
limit: "aggregateRawSnapshotBytes",
|
|
993
|
+
actual: nextAggregate,
|
|
994
|
+
}),
|
|
995
|
+
], { ...endpointFields, resolvedLowerBoundary });
|
|
996
|
+
}
|
|
997
|
+
aggregateRawSnapshotBytes = nextAggregate;
|
|
998
|
+
source = new Uint8Array(sourceCommand.stdout);
|
|
999
|
+
sourceDigest = digestDocumentBytes(source);
|
|
1000
|
+
}
|
|
1001
|
+
pending.push(Object.freeze({
|
|
1002
|
+
commitId: inspectedCommitId,
|
|
1003
|
+
parentCommitIds: Object.freeze([...metadata.parentCommitIds]),
|
|
1004
|
+
blobId,
|
|
1005
|
+
sourceDigest,
|
|
1006
|
+
source,
|
|
1007
|
+
recordedAt: metadata.recordedAt,
|
|
1008
|
+
isEndpoint: inspectedCommitId === resolvedEndpoint,
|
|
1009
|
+
isLowerBoundary: inspectedCommitId === resolvedLowerBoundary,
|
|
1010
|
+
}));
|
|
1011
|
+
}
|
|
1012
|
+
const readSnapshotDigest = digestDocumentBytes(Buffer.from(JSON.stringify({
|
|
1013
|
+
model: "Perttool.HistoricalGitEvidence.v1",
|
|
1014
|
+
objectFormat,
|
|
1015
|
+
repositoryId,
|
|
1016
|
+
repositoryRelativePath: relativePath,
|
|
1017
|
+
resolvedEndpoint,
|
|
1018
|
+
resolvedLowerBoundary,
|
|
1019
|
+
snapshots: pending.map((snapshot) => ({
|
|
1020
|
+
commitId: snapshot.commitId,
|
|
1021
|
+
parentCommitIds: snapshot.parentCommitIds,
|
|
1022
|
+
blobId: snapshot.blobId,
|
|
1023
|
+
sourceDigest: snapshot.sourceDigest,
|
|
1024
|
+
})),
|
|
1025
|
+
}), "utf8"));
|
|
1026
|
+
const repositoryReadSnapshotId = `git-read:${readSnapshotDigest}`;
|
|
1027
|
+
if (dependencies.afterEvidence !== undefined) {
|
|
1028
|
+
await dependencies.afterEvidence();
|
|
1029
|
+
}
|
|
1030
|
+
const finalEndpoint = resolveHistoricalRevision(executable, repositoryRoot, requestedEndpoint, objectFormat, "historical_recheck_endpoint");
|
|
1031
|
+
if (!finalEndpoint.ok) {
|
|
1032
|
+
if ("failure" in finalEndpoint)
|
|
1033
|
+
return finalEndpoint.failure;
|
|
1034
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("repository_race", "endpoint")], { ...endpointFields, resolvedLowerBoundary });
|
|
1035
|
+
}
|
|
1036
|
+
let finalLowerCommit = null;
|
|
1037
|
+
if (request.lowerBoundary !== undefined) {
|
|
1038
|
+
const finalLower = resolveHistoricalRevision(executable, repositoryRoot, request.lowerBoundary, objectFormat, "historical_recheck_lower");
|
|
1039
|
+
if (!finalLower.ok) {
|
|
1040
|
+
if ("failure" in finalLower)
|
|
1041
|
+
return finalLower.failure;
|
|
1042
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("repository_race", "lower_boundary")], { ...endpointFields, resolvedLowerBoundary });
|
|
1043
|
+
}
|
|
1044
|
+
finalLowerCommit = finalLower.commitId;
|
|
1045
|
+
}
|
|
1046
|
+
const finalTarget = await captureTarget(targetPath);
|
|
1047
|
+
let finalCommonDirectory = null;
|
|
1048
|
+
try {
|
|
1049
|
+
finalCommonDirectory = await realpath(commonText);
|
|
1050
|
+
}
|
|
1051
|
+
catch {
|
|
1052
|
+
finalCommonDirectory = null;
|
|
1053
|
+
}
|
|
1054
|
+
if (finalEndpoint.commitId !== resolvedEndpoint ||
|
|
1055
|
+
finalLowerCommit !== resolvedLowerBoundary ||
|
|
1056
|
+
finalCommonDirectory !== commonDirectory ||
|
|
1057
|
+
!finalTarget.ok ||
|
|
1058
|
+
!sameTarget(initialTarget.capture, finalTarget.capture)) {
|
|
1059
|
+
return historicalEvidenceResult(request, limits, "unavailable", [historicalCause("repository_race", "repository")], { ...endpointFields, resolvedLowerBoundary });
|
|
1060
|
+
}
|
|
1061
|
+
const snapshots = pending.map((snapshot) => Object.freeze({
|
|
1062
|
+
modelVersion: HISTORICAL_GIT_EVIDENCE_MODEL_VERSION,
|
|
1063
|
+
objectFormat,
|
|
1064
|
+
repositoryId,
|
|
1065
|
+
repositoryReadSnapshotId,
|
|
1066
|
+
repositoryRelativePath: relativePath,
|
|
1067
|
+
commitId: snapshot.commitId,
|
|
1068
|
+
parentCommitIds: snapshot.parentCommitIds,
|
|
1069
|
+
blobId: snapshot.blobId,
|
|
1070
|
+
sourceDigest: snapshot.sourceDigest,
|
|
1071
|
+
source: snapshot.source,
|
|
1072
|
+
recordedAt: snapshot.recordedAt,
|
|
1073
|
+
isMergeCommit: snapshot.parentCommitIds.length > 1,
|
|
1074
|
+
isEndpoint: snapshot.isEndpoint,
|
|
1075
|
+
isLowerBoundary: snapshot.isLowerBoundary,
|
|
1076
|
+
}));
|
|
1077
|
+
const causes = shallow && resolvedLowerBoundary === null
|
|
1078
|
+
? [
|
|
1079
|
+
historicalCause("shallow_origin", "repository", {
|
|
1080
|
+
commitId: inspectedCommitIds[0] ?? null,
|
|
1081
|
+
}),
|
|
1082
|
+
]
|
|
1083
|
+
: [];
|
|
1084
|
+
return historicalEvidenceResult(request, limits, causes.length === 0 ? "complete" : "incomplete", causes, {
|
|
1085
|
+
...endpointFields,
|
|
1086
|
+
repositoryReadSnapshotId,
|
|
1087
|
+
resolvedLowerBoundary,
|
|
1088
|
+
oldestInspectedCommitId: inspectedCommitIds[0] ?? null,
|
|
1089
|
+
aggregateRawSnapshotBytes,
|
|
1090
|
+
inspectedCommitIds,
|
|
1091
|
+
snapshots,
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
577
1094
|
function unavailableAdvanceBaseline(cause, operation, fields = {}) {
|
|
578
1095
|
return {
|
|
579
1096
|
ok: true,
|