@fern-api/fern-api-dev 5.0.0-6-gb0a2ec4ace2 → 5.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/cli.cjs +841 -92
- package/package.json +1 -1
package/cli.cjs
CHANGED
|
@@ -340394,9 +340394,9 @@ var require_diff3 = __commonJS({
|
|
|
340394
340394
|
}
|
|
340395
340395
|
});
|
|
340396
340396
|
|
|
340397
|
-
// ../../../node_modules/.pnpm/@fern-api+replay@0.
|
|
340397
|
+
// ../../../node_modules/.pnpm/@fern-api+replay@0.14.0/node_modules/@fern-api/replay/dist/index.cjs
|
|
340398
340398
|
var require_dist14 = __commonJS({
|
|
340399
|
-
"../../../node_modules/.pnpm/@fern-api+replay@0.
|
|
340399
|
+
"../../../node_modules/.pnpm/@fern-api+replay@0.14.0/node_modules/@fern-api/replay/dist/index.cjs"(exports2, module4) {
|
|
340400
340400
|
"use strict";
|
|
340401
340401
|
var __defProp4 = Object.defineProperty;
|
|
340402
340402
|
var __getOwnPropDesc4 = Object.getOwnPropertyDescriptor;
|
|
@@ -340439,9 +340439,9 @@ var require_dist14 = __commonJS({
|
|
|
340439
340439
|
return this.git.raw(args);
|
|
340440
340440
|
}
|
|
340441
340441
|
async execWithInput(args, input3) {
|
|
340442
|
-
const { spawn:
|
|
340442
|
+
const { spawn: spawn22 } = await import("child_process");
|
|
340443
340443
|
return new Promise((resolve22, reject) => {
|
|
340444
|
-
const proc2 =
|
|
340444
|
+
const proc2 = spawn22("git", args, { cwd: this.repoPath });
|
|
340445
340445
|
let stdout = "";
|
|
340446
340446
|
let stderr = "";
|
|
340447
340447
|
proc2.stdout.on("data", (data2) => {
|
|
@@ -341684,11 +341684,43 @@ var require_dist14 = __commonJS({
|
|
|
341684
341684
|
}
|
|
341685
341685
|
}
|
|
341686
341686
|
}
|
|
341687
|
-
|
|
341687
|
+
const result = {
|
|
341688
341688
|
content: outputLines.join("\n"),
|
|
341689
341689
|
hasConflicts: conflicts2.length > 0,
|
|
341690
341690
|
conflicts: conflicts2
|
|
341691
341691
|
};
|
|
341692
|
+
if (conflicts2.length === 0) {
|
|
341693
|
+
const dropped = computeDroppedContextLines(baseLines, theirsLines, outputLines);
|
|
341694
|
+
if (dropped.length > 0) {
|
|
341695
|
+
result.droppedContextLines = dropped;
|
|
341696
|
+
}
|
|
341697
|
+
}
|
|
341698
|
+
return result;
|
|
341699
|
+
}
|
|
341700
|
+
function computeDroppedContextLines(baseLines, theirsLines, mergedLines) {
|
|
341701
|
+
const baseCounts = countLines2(baseLines);
|
|
341702
|
+
const theirsCounts = countLines2(theirsLines);
|
|
341703
|
+
const mergedCounts = countLines2(mergedLines);
|
|
341704
|
+
const dropped = [];
|
|
341705
|
+
const seen = /* @__PURE__ */ new Set();
|
|
341706
|
+
for (const line of baseLines) {
|
|
341707
|
+
if (seen.has(line)) continue;
|
|
341708
|
+
const inBase = baseCounts.get(line) ?? 0;
|
|
341709
|
+
const inTheirs = theirsCounts.get(line) ?? 0;
|
|
341710
|
+
const inMerged = mergedCounts.get(line) ?? 0;
|
|
341711
|
+
if (inBase > 0 && inTheirs > 0 && inMerged < Math.min(inBase, inTheirs)) {
|
|
341712
|
+
dropped.push(line);
|
|
341713
|
+
seen.add(line);
|
|
341714
|
+
}
|
|
341715
|
+
}
|
|
341716
|
+
return dropped;
|
|
341717
|
+
}
|
|
341718
|
+
function countLines2(lines) {
|
|
341719
|
+
const counts = /* @__PURE__ */ new Map();
|
|
341720
|
+
for (const line of lines) {
|
|
341721
|
+
counts.set(line, (counts.get(line) ?? 0) + 1);
|
|
341722
|
+
}
|
|
341723
|
+
return counts;
|
|
341692
341724
|
}
|
|
341693
341725
|
function tryResolveConflict(oursLines, baseLines, theirsLines) {
|
|
341694
341726
|
if (baseLines.length === 0) {
|
|
@@ -341877,6 +341909,19 @@ var require_dist14 = __commonJS({
|
|
|
341877
341909
|
renameCache = /* @__PURE__ */ new Map();
|
|
341878
341910
|
treeExistsCache = /* @__PURE__ */ new Map();
|
|
341879
341911
|
fileTheirsAccumulator = /* @__PURE__ */ new Map();
|
|
341912
|
+
/**
|
|
341913
|
+
* Apply mode for the current `applyPatches` invocation. Set at the start
|
|
341914
|
+
* of `applyPatches`, read by `mergeFile` to decide:
|
|
341915
|
+
* - whether to skip the intra-loop marker strip (kept in `applyPatches`)
|
|
341916
|
+
* - whether to populate the accumulator after a conflicted merge
|
|
341917
|
+
* (resolve mode populates so subsequent patches on the same file
|
|
341918
|
+
* can use patch A's THEIRS as a structurally-correct merge base
|
|
341919
|
+
* when their diff was authored against post-A structure)
|
|
341920
|
+
* - whether to retry THEIRS reconstruction against the accumulator
|
|
341921
|
+
* when the BASE-relative reconstruction produced markers from a
|
|
341922
|
+
* cross-patch context mismatch
|
|
341923
|
+
*/
|
|
341924
|
+
currentApplyMode = "replay";
|
|
341880
341925
|
constructor(git, lockManager, outputDir) {
|
|
341881
341926
|
this.git = git;
|
|
341882
341927
|
this.lockManager = lockManager;
|
|
@@ -341924,8 +341969,23 @@ var require_dist14 = __commonJS({
|
|
|
341924
341969
|
/**
|
|
341925
341970
|
* Apply all patches, returning results for each.
|
|
341926
341971
|
* Skips patches that match exclude patterns in replay.yml
|
|
341927
|
-
|
|
341928
|
-
|
|
341972
|
+
*
|
|
341973
|
+
* `applyMode` controls the post-conflict marker strategy:
|
|
341974
|
+
* - `"replay"` (default): strip conflict markers from disk between
|
|
341975
|
+
* iterations whenever a later patch touches the same file. Lets the
|
|
341976
|
+
* follow-up patch's `git apply --3way` see clean OURS content,
|
|
341977
|
+
* which is what the silent-loss fix (PR #73) relies on. The replay
|
|
341978
|
+
* pipeline calls `revertConflictingFiles` after the loop to clean
|
|
341979
|
+
* any markers that survive.
|
|
341980
|
+
* - `"resolve"`: keep markers on disk. The resolve command needs the
|
|
341981
|
+
* customer to see them. Slow-path 3-way merge naturally preserves
|
|
341982
|
+
* A's markers and B's clean writes when their regions don't overlap;
|
|
341983
|
+
* when they do overlap, the customer gets nested markers and
|
|
341984
|
+
* resolves manually. Either way they aren't silently dropped.
|
|
341985
|
+
*/
|
|
341986
|
+
async applyPatches(patches, opts) {
|
|
341987
|
+
const applyMode = opts?.applyMode ?? "replay";
|
|
341988
|
+
this.currentApplyMode = applyMode;
|
|
341929
341989
|
this.resetAccumulator();
|
|
341930
341990
|
const results = [];
|
|
341931
341991
|
for (let i9 = 0; i9 < patches.length; i9++) {
|
|
@@ -341940,7 +342000,7 @@ var require_dist14 = __commonJS({
|
|
|
341940
342000
|
}
|
|
341941
342001
|
const result = await this.applyPatchWithFallback(patch5);
|
|
341942
342002
|
results.push(result);
|
|
341943
|
-
if (result.status === "conflict" && result.fileResults) {
|
|
342003
|
+
if (applyMode !== "resolve" && result.status === "conflict" && result.fileResults) {
|
|
341944
342004
|
const laterFiles = /* @__PURE__ */ new Set();
|
|
341945
342005
|
for (let j7 = i9 + 1; j7 < patches.length; j7++) {
|
|
341946
342006
|
for (const f5 of patches[j7].files) {
|
|
@@ -341970,9 +342030,17 @@ var require_dist14 = __commonJS({
|
|
|
341970
342030
|
}
|
|
341971
342031
|
return results;
|
|
341972
342032
|
}
|
|
341973
|
-
/**
|
|
342033
|
+
/**
|
|
342034
|
+
* Populate accumulator after git apply succeeds, AND collect per-file
|
|
342035
|
+
* results including any "dropped context lines" — lines that were
|
|
342036
|
+
* present in BASE and THEIRS (unchanged context) but absent from the
|
|
342037
|
+
* final on-disk state because OURS deleted them. This is the fast-path
|
|
342038
|
+
* counterpart to ThreeWayMerge.computeDroppedContextLines used by the
|
|
342039
|
+
* 3-way slow path; both must surface the same warning.
|
|
342040
|
+
*/
|
|
341974
342041
|
async populateAccumulatorForPatch(patch5, baseGen, currentTreeHash) {
|
|
341975
|
-
|
|
342042
|
+
const fileResults = [];
|
|
342043
|
+
if (!baseGen) return fileResults;
|
|
341976
342044
|
const tempDir2 = await (0, import_promises192.mkdtemp)((0, import_node_path210.join)((0, import_node_os15.tmpdir)(), "replay-acc-"));
|
|
341977
342045
|
const { GitClient: GitClient2 } = await Promise.resolve().then(() => (init_GitClient(), GitClient_exports));
|
|
341978
342046
|
const tempGit = new GitClient2(tempDir2);
|
|
@@ -341985,18 +342053,30 @@ var require_dist14 = __commonJS({
|
|
|
341985
342053
|
const resolvedPath = await this.resolveFilePath(filePath, baseGen.tree_hash, currentTreeHash);
|
|
341986
342054
|
const base4 = await this.git.showFile(baseGen.tree_hash, filePath);
|
|
341987
342055
|
const theirs = await this.applyPatchToContent(base4, patch5.patch_content, filePath, tempGit, tempDir2);
|
|
341988
|
-
const
|
|
342056
|
+
const finalOnDisk = await (0, import_promises192.readFile)((0, import_node_path210.join)(this.outputDir, resolvedPath), "utf-8").catch(() => null);
|
|
342057
|
+
const effectiveTheirs = theirs ?? finalOnDisk;
|
|
341989
342058
|
if (effectiveTheirs != null) {
|
|
341990
342059
|
this.fileTheirsAccumulator.set(resolvedPath, {
|
|
341991
342060
|
content: effectiveTheirs,
|
|
341992
342061
|
baseGeneration: patch5.base_generation
|
|
341993
342062
|
});
|
|
341994
342063
|
}
|
|
342064
|
+
if (base4 != null && effectiveTheirs != null && finalOnDisk != null) {
|
|
342065
|
+
const dropped = computeDroppedContextLinesForFile(base4, effectiveTheirs, finalOnDisk);
|
|
342066
|
+
if (dropped.length > 0) {
|
|
342067
|
+
fileResults.push({
|
|
342068
|
+
file: resolvedPath,
|
|
342069
|
+
status: "merged",
|
|
342070
|
+
droppedContextLines: dropped
|
|
342071
|
+
});
|
|
342072
|
+
}
|
|
342073
|
+
}
|
|
341995
342074
|
}
|
|
341996
342075
|
} finally {
|
|
341997
342076
|
await (0, import_promises192.rm)(tempDir2, { recursive: true }).catch(() => {
|
|
341998
342077
|
});
|
|
341999
342078
|
}
|
|
342079
|
+
return fileResults;
|
|
342000
342080
|
}
|
|
342001
342081
|
async applyPatchWithFallback(patch5) {
|
|
342002
342082
|
const baseGen = await this.resolveBaseGeneration(patch5.base_generation);
|
|
@@ -342027,11 +342107,12 @@ var require_dist14 = __commonJS({
|
|
|
342027
342107
|
}
|
|
342028
342108
|
try {
|
|
342029
342109
|
await this.git.execWithInput(["apply", "--3way"], patch5.patch_content);
|
|
342030
|
-
await this.populateAccumulatorForPatch(patch5, baseGen, currentTreeHash);
|
|
342110
|
+
const fastPathFileResults = await this.populateAccumulatorForPatch(patch5, baseGen, currentTreeHash);
|
|
342031
342111
|
return {
|
|
342032
342112
|
patch: patch5,
|
|
342033
342113
|
status: "applied",
|
|
342034
342114
|
method: "git-am",
|
|
342115
|
+
...fastPathFileResults.length > 0 && { fileResults: fastPathFileResults },
|
|
342035
342116
|
...Object.keys(resolvedFiles).length > 0 && { resolvedFiles }
|
|
342036
342117
|
};
|
|
342037
342118
|
} catch {
|
|
@@ -342143,19 +342224,23 @@ var require_dist14 = __commonJS({
|
|
|
342143
342224
|
renameSourcePath
|
|
342144
342225
|
);
|
|
342145
342226
|
}
|
|
342227
|
+
const accumulatorEntry = this.fileTheirsAccumulator.get(resolvedPath);
|
|
342146
342228
|
if (theirs) {
|
|
342147
342229
|
const theirsHasMarkers = theirs.includes("<<<<<<< Generated") || theirs.includes(">>>>>>> Your customization");
|
|
342148
342230
|
const baseHasMarkers = base4 != null && (base4.includes("<<<<<<< Generated") || base4.includes(">>>>>>> Your customization"));
|
|
342149
342231
|
if (theirsHasMarkers && !baseHasMarkers) {
|
|
342150
|
-
|
|
342151
|
-
|
|
342152
|
-
|
|
342153
|
-
|
|
342154
|
-
|
|
342232
|
+
if (accumulatorEntry) {
|
|
342233
|
+
theirs = null;
|
|
342234
|
+
} else {
|
|
342235
|
+
return {
|
|
342236
|
+
file: resolvedPath,
|
|
342237
|
+
status: "skipped",
|
|
342238
|
+
reason: "stale-conflict-markers"
|
|
342239
|
+
};
|
|
342240
|
+
}
|
|
342155
342241
|
}
|
|
342156
342242
|
}
|
|
342157
342243
|
let useAccumulatorAsMergeBase = false;
|
|
342158
|
-
const accumulatorEntry = this.fileTheirsAccumulator.get(resolvedPath);
|
|
342159
342244
|
if (accumulatorEntry && (theirs == null || base4 == null)) {
|
|
342160
342245
|
theirs = await this.applyPatchToContent(
|
|
342161
342246
|
accumulatorEntry.content,
|
|
@@ -342262,7 +342347,8 @@ var require_dist14 = __commonJS({
|
|
|
342262
342347
|
const outDir = (0, import_node_path210.dirname)(oursPath);
|
|
342263
342348
|
await (0, import_promises192.mkdir)(outDir, { recursive: true });
|
|
342264
342349
|
await (0, import_promises192.writeFile)(oursPath, merged.content);
|
|
342265
|
-
|
|
342350
|
+
const populateAccumulator = effective_theirs != null && (!merged.hasConflicts || this.currentApplyMode === "resolve");
|
|
342351
|
+
if (populateAccumulator) {
|
|
342266
342352
|
this.fileTheirsAccumulator.set(resolvedPath, {
|
|
342267
342353
|
content: effective_theirs,
|
|
342268
342354
|
baseGeneration: patch5.base_generation
|
|
@@ -342277,7 +342363,11 @@ var require_dist14 = __commonJS({
|
|
|
342277
342363
|
conflictMetadata: metadata
|
|
342278
342364
|
};
|
|
342279
342365
|
}
|
|
342280
|
-
return {
|
|
342366
|
+
return {
|
|
342367
|
+
file: resolvedPath,
|
|
342368
|
+
status: "merged",
|
|
342369
|
+
...merged.droppedContextLines && merged.droppedContextLines.length > 0 ? { droppedContextLines: merged.droppedContextLines } : {}
|
|
342370
|
+
};
|
|
342281
342371
|
} catch (error50) {
|
|
342282
342372
|
return {
|
|
342283
342373
|
file: filePath,
|
|
@@ -342463,6 +342553,30 @@ var require_dist14 = __commonJS({
|
|
|
342463
342553
|
const ext2 = (0, import_node_path210.extname)(filePath).toLowerCase();
|
|
342464
342554
|
return BINARY_EXTENSIONS2.has(ext2);
|
|
342465
342555
|
}
|
|
342556
|
+
function computeDroppedContextLinesForFile(base4, theirs, finalContent) {
|
|
342557
|
+
const baseLines = base4.split("\n");
|
|
342558
|
+
const theirsLines = theirs.split("\n");
|
|
342559
|
+
const finalLines = finalContent.split("\n");
|
|
342560
|
+
const baseCounts = /* @__PURE__ */ new Map();
|
|
342561
|
+
const theirsCounts = /* @__PURE__ */ new Map();
|
|
342562
|
+
const finalCounts = /* @__PURE__ */ new Map();
|
|
342563
|
+
for (const l9 of baseLines) baseCounts.set(l9, (baseCounts.get(l9) ?? 0) + 1);
|
|
342564
|
+
for (const l9 of theirsLines) theirsCounts.set(l9, (theirsCounts.get(l9) ?? 0) + 1);
|
|
342565
|
+
for (const l9 of finalLines) finalCounts.set(l9, (finalCounts.get(l9) ?? 0) + 1);
|
|
342566
|
+
const dropped = [];
|
|
342567
|
+
const seen = /* @__PURE__ */ new Set();
|
|
342568
|
+
for (const line of baseLines) {
|
|
342569
|
+
if (seen.has(line)) continue;
|
|
342570
|
+
const inBase = baseCounts.get(line) ?? 0;
|
|
342571
|
+
const inTheirs = theirsCounts.get(line) ?? 0;
|
|
342572
|
+
const inFinal = finalCounts.get(line) ?? 0;
|
|
342573
|
+
if (inBase > 0 && inTheirs > 0 && inFinal < Math.min(inBase, inTheirs)) {
|
|
342574
|
+
dropped.push(line);
|
|
342575
|
+
seen.add(line);
|
|
342576
|
+
}
|
|
342577
|
+
}
|
|
342578
|
+
return dropped;
|
|
342579
|
+
}
|
|
342466
342580
|
function isDiffLineForFile(diffLine, filePath) {
|
|
342467
342581
|
const match3 = diffLine.match(/^diff --git a\/.+ b\/(.+)$/);
|
|
342468
342582
|
return match3 !== null && match3[1] === filePath;
|
|
@@ -342496,13 +342610,46 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
342496
342610
|
await this.git.exec(["commit", "-m", fullMessage]);
|
|
342497
342611
|
return (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
342498
342612
|
}
|
|
342499
|
-
async commitReplay(_patchCount, patches, message) {
|
|
342613
|
+
async commitReplay(_patchCount, patches, message, options2) {
|
|
342500
342614
|
await this.stageAll();
|
|
342501
342615
|
if (!await this.hasStagedChanges()) {
|
|
342502
342616
|
return (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
342503
342617
|
}
|
|
342504
342618
|
let fullMessage = message ?? `[fern-replay] Applied customizations`;
|
|
342505
|
-
|
|
342619
|
+
const buckets = options2?.buckets;
|
|
342620
|
+
if (buckets) {
|
|
342621
|
+
if (buckets.applied.length > 0) {
|
|
342622
|
+
fullMessage += `
|
|
342623
|
+
|
|
342624
|
+
Patches applied (${buckets.applied.length}):`;
|
|
342625
|
+
for (const p14 of buckets.applied) {
|
|
342626
|
+
fullMessage += `
|
|
342627
|
+
- ${p14.id}: ${p14.original_message}`;
|
|
342628
|
+
}
|
|
342629
|
+
}
|
|
342630
|
+
if (buckets.unresolved.length > 0) {
|
|
342631
|
+
fullMessage += `
|
|
342632
|
+
|
|
342633
|
+
Patches with unresolved conflicts (${buckets.unresolved.length}):`;
|
|
342634
|
+
for (const p14 of buckets.unresolved) {
|
|
342635
|
+
fullMessage += `
|
|
342636
|
+
- ${p14.id}: ${p14.original_message}`;
|
|
342637
|
+
}
|
|
342638
|
+
fullMessage += `
|
|
342639
|
+
Run \`fern-replay resolve\` to apply these customizations.`;
|
|
342640
|
+
}
|
|
342641
|
+
if (buckets.absorbed.length > 0) {
|
|
342642
|
+
fullMessage += `
|
|
342643
|
+
|
|
342644
|
+
Patches absorbed by generator (${buckets.absorbed.length}):`;
|
|
342645
|
+
for (const p14 of buckets.absorbed) {
|
|
342646
|
+
fullMessage += `
|
|
342647
|
+
- ${p14.id}: ${p14.original_message}`;
|
|
342648
|
+
}
|
|
342649
|
+
fullMessage += `
|
|
342650
|
+
The generator now produces these customizations natively.`;
|
|
342651
|
+
}
|
|
342652
|
+
} else if (patches && patches.length > 0) {
|
|
342506
342653
|
fullMessage += "\n\nPatches replayed:";
|
|
342507
342654
|
for (const patch5 of patches) {
|
|
342508
342655
|
fullMessage += `
|
|
@@ -342536,9 +342683,299 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
342536
342683
|
}
|
|
342537
342684
|
};
|
|
342538
342685
|
var import_node_fs210 = require("fs");
|
|
342539
|
-
var
|
|
342686
|
+
var import_node_path43 = require("path");
|
|
342540
342687
|
var import_minimatch22 = require_commonjs3();
|
|
342541
342688
|
init_GitClient();
|
|
342689
|
+
var import_promises210 = require("fs/promises");
|
|
342690
|
+
var import_node_os22 = require("os");
|
|
342691
|
+
var import_node_path310 = require("path");
|
|
342692
|
+
var import_node_child_process7 = require("child_process");
|
|
342693
|
+
init_HybridReconstruction();
|
|
342694
|
+
function normalizeHunkBody(hunk) {
|
|
342695
|
+
let contextC = 0;
|
|
342696
|
+
let removeC = 0;
|
|
342697
|
+
let addC = 0;
|
|
342698
|
+
const trimmed2 = [];
|
|
342699
|
+
for (const line of hunk.lines) {
|
|
342700
|
+
const oldUsed = contextC + removeC;
|
|
342701
|
+
const newUsed = contextC + addC;
|
|
342702
|
+
if (oldUsed >= hunk.oldCount && newUsed >= hunk.newCount) break;
|
|
342703
|
+
trimmed2.push(line);
|
|
342704
|
+
if (line.type === "context") contextC++;
|
|
342705
|
+
else if (line.type === "remove") removeC++;
|
|
342706
|
+
else if (line.type === "add") addC++;
|
|
342707
|
+
}
|
|
342708
|
+
return { ...hunk, lines: trimmed2 };
|
|
342709
|
+
}
|
|
342710
|
+
function extractFileDiffForFile(patchContent, filePath) {
|
|
342711
|
+
const lines = patchContent.split("\n");
|
|
342712
|
+
const out = [];
|
|
342713
|
+
let inTarget = false;
|
|
342714
|
+
for (const line of lines) {
|
|
342715
|
+
if (line.startsWith("diff --git")) {
|
|
342716
|
+
if (inTarget) break;
|
|
342717
|
+
if (isDiffLineForFile2(line, filePath)) {
|
|
342718
|
+
inTarget = true;
|
|
342719
|
+
out.push(line);
|
|
342720
|
+
}
|
|
342721
|
+
continue;
|
|
342722
|
+
}
|
|
342723
|
+
if (inTarget) out.push(line);
|
|
342724
|
+
}
|
|
342725
|
+
return out.length > 0 ? out.join("\n") + "\n" : null;
|
|
342726
|
+
}
|
|
342727
|
+
function isDiffLineForFile2(line, filePath) {
|
|
342728
|
+
const match3 = line.match(/^diff --git a\/(.+) b\/(.+)$/);
|
|
342729
|
+
return match3 !== null && (match3[2] === filePath || match3[1] === filePath);
|
|
342730
|
+
}
|
|
342731
|
+
async function computePerPatchDiff(patch5, getCurrentGenContent, getWorkingTreeContent) {
|
|
342732
|
+
const fragments = [];
|
|
342733
|
+
const unlocatableFiles = [];
|
|
342734
|
+
for (const file4 of patch5.files) {
|
|
342735
|
+
const fileDiff = extractFileDiffForFile(patch5.patch_content, file4);
|
|
342736
|
+
if (fileDiff == null) {
|
|
342737
|
+
unlocatableFiles.push(file4);
|
|
342738
|
+
continue;
|
|
342739
|
+
}
|
|
342740
|
+
const hunksRaw = parseHunks(fileDiff).map(normalizeHunkBody);
|
|
342741
|
+
const hunks = hunksRaw.filter((h4) => {
|
|
342742
|
+
if (h4.lines.length === 0) return false;
|
|
342743
|
+
let ctx = 0, rm32 = 0, add = 0;
|
|
342744
|
+
for (const ln10 of h4.lines) {
|
|
342745
|
+
if (ln10.type === "context") ctx++;
|
|
342746
|
+
else if (ln10.type === "remove") rm32++;
|
|
342747
|
+
else if (ln10.type === "add") add++;
|
|
342748
|
+
}
|
|
342749
|
+
return ctx + rm32 === h4.oldCount && ctx + add === h4.newCount;
|
|
342750
|
+
});
|
|
342751
|
+
if (hunks.length === 0) {
|
|
342752
|
+
if (hunksRaw.length > 0) unlocatableFiles.push(file4);
|
|
342753
|
+
continue;
|
|
342754
|
+
}
|
|
342755
|
+
const currentGen = await getCurrentGenContent(file4);
|
|
342756
|
+
const workingTree = await getWorkingTreeContent(file4);
|
|
342757
|
+
const isPureNewFile = hunks.every((h4) => h4.oldCount === 0);
|
|
342758
|
+
const isPureDelete = hunks.every((h4) => h4.newCount === 0);
|
|
342759
|
+
if (currentGen == null) {
|
|
342760
|
+
if (!isPureNewFile) {
|
|
342761
|
+
unlocatableFiles.push(file4);
|
|
342762
|
+
continue;
|
|
342763
|
+
}
|
|
342764
|
+
if (workingTree == null) continue;
|
|
342765
|
+
fragments.push(synthesizeNewFileDiff(file4, workingTree));
|
|
342766
|
+
continue;
|
|
342767
|
+
}
|
|
342768
|
+
if (isPureNewFile) {
|
|
342769
|
+
if (workingTree == null) {
|
|
342770
|
+
fragments.push(synthesizeDeleteFileDiff(file4, currentGen));
|
|
342771
|
+
continue;
|
|
342772
|
+
}
|
|
342773
|
+
if (workingTree === currentGen) {
|
|
342774
|
+
continue;
|
|
342775
|
+
}
|
|
342776
|
+
const fullDiff = await unifiedDiff(currentGen, workingTree, file4);
|
|
342777
|
+
if (fullDiff && fullDiff.trim()) fragments.push(fullDiff);
|
|
342778
|
+
continue;
|
|
342779
|
+
}
|
|
342780
|
+
if (workingTree == null) {
|
|
342781
|
+
if (!isPureDelete) {
|
|
342782
|
+
unlocatableFiles.push(file4);
|
|
342783
|
+
continue;
|
|
342784
|
+
}
|
|
342785
|
+
fragments.push(synthesizeDeleteFileDiff(file4, currentGen));
|
|
342786
|
+
continue;
|
|
342787
|
+
}
|
|
342788
|
+
const currentGenLines = splitLines(currentGen);
|
|
342789
|
+
const workingTreeLines = splitLines(workingTree);
|
|
342790
|
+
const locInCurrentGenRaw = locateHunksInOurs(hunks, currentGenLines);
|
|
342791
|
+
const locInWorkingTreeRaw = locateHunksInOurs(hunks, workingTreeLines);
|
|
342792
|
+
if (locInCurrentGenRaw == null || locInWorkingTreeRaw == null) {
|
|
342793
|
+
unlocatableFiles.push(file4);
|
|
342794
|
+
continue;
|
|
342795
|
+
}
|
|
342796
|
+
const locInCurrentGen = locInCurrentGenRaw.map(
|
|
342797
|
+
(l9) => resolveSpan(l9, currentGenLines, "old")
|
|
342798
|
+
);
|
|
342799
|
+
const locInWorkingTree = locInWorkingTreeRaw.map(
|
|
342800
|
+
(l9) => resolveSpan(l9, workingTreeLines, "new")
|
|
342801
|
+
);
|
|
342802
|
+
const overlay = overlayRegions(
|
|
342803
|
+
currentGenLines,
|
|
342804
|
+
locInCurrentGen,
|
|
342805
|
+
workingTreeLines,
|
|
342806
|
+
locInWorkingTree
|
|
342807
|
+
);
|
|
342808
|
+
if (overlay === null) {
|
|
342809
|
+
unlocatableFiles.push(file4);
|
|
342810
|
+
continue;
|
|
342811
|
+
}
|
|
342812
|
+
if (overlay === currentGen) {
|
|
342813
|
+
continue;
|
|
342814
|
+
}
|
|
342815
|
+
const fileUnifiedDiff = await unifiedDiff(currentGen, overlay, file4);
|
|
342816
|
+
if (fileUnifiedDiff && fileUnifiedDiff.trim()) {
|
|
342817
|
+
fragments.push(fileUnifiedDiff);
|
|
342818
|
+
}
|
|
342819
|
+
}
|
|
342820
|
+
return {
|
|
342821
|
+
diff: fragments.join(""),
|
|
342822
|
+
unlocatableFiles
|
|
342823
|
+
};
|
|
342824
|
+
}
|
|
342825
|
+
async function computePerPatchDiffWithFallback(patch5, getCurrentGenContent, getWorkingTreeContent, runCumulativeDiff) {
|
|
342826
|
+
const ppDiff = await computePerPatchDiff(
|
|
342827
|
+
patch5,
|
|
342828
|
+
getCurrentGenContent,
|
|
342829
|
+
getWorkingTreeContent
|
|
342830
|
+
);
|
|
342831
|
+
if (ppDiff.unlocatableFiles.length === 0) {
|
|
342832
|
+
return { diff: ppDiff.diff, hadFallback: false, fallbackFiles: [] };
|
|
342833
|
+
}
|
|
342834
|
+
const cumulative = await runCumulativeDiff(ppDiff.unlocatableFiles);
|
|
342835
|
+
if (cumulative === null) {
|
|
342836
|
+
return null;
|
|
342837
|
+
}
|
|
342838
|
+
const merged = ppDiff.diff + (cumulative.trim() ? cumulative : "");
|
|
342839
|
+
return { diff: merged, hadFallback: true, fallbackFiles: ppDiff.unlocatableFiles };
|
|
342840
|
+
}
|
|
342841
|
+
function splitLines(content5) {
|
|
342842
|
+
return content5.split("\n");
|
|
342843
|
+
}
|
|
342844
|
+
function resolveSpan(loc, oursLines, prefer) {
|
|
342845
|
+
const { hunk, oursOffset } = loc;
|
|
342846
|
+
const oldSide = [];
|
|
342847
|
+
const newSide = [];
|
|
342848
|
+
for (const line of hunk.lines) {
|
|
342849
|
+
if (line.type === "context") {
|
|
342850
|
+
oldSide.push(line.content);
|
|
342851
|
+
newSide.push(line.content);
|
|
342852
|
+
} else if (line.type === "remove") {
|
|
342853
|
+
oldSide.push(line.content);
|
|
342854
|
+
} else if (line.type === "add") {
|
|
342855
|
+
newSide.push(line.content);
|
|
342856
|
+
}
|
|
342857
|
+
}
|
|
342858
|
+
const oldFits = sequenceMatches(oldSide, oursLines, oursOffset);
|
|
342859
|
+
const newFits = sequenceMatches(newSide, oursLines, oursOffset);
|
|
342860
|
+
if (prefer === "old") {
|
|
342861
|
+
if (oldFits) return { ...loc, oursSpan: hunk.oldCount };
|
|
342862
|
+
if (newFits) return { ...loc, oursSpan: hunk.newCount };
|
|
342863
|
+
} else {
|
|
342864
|
+
if (newFits) return { ...loc, oursSpan: hunk.newCount };
|
|
342865
|
+
if (oldFits) return { ...loc, oursSpan: hunk.oldCount };
|
|
342866
|
+
}
|
|
342867
|
+
return loc;
|
|
342868
|
+
}
|
|
342869
|
+
function sequenceMatches(needle, haystack, offset2) {
|
|
342870
|
+
if (offset2 + needle.length > haystack.length) return false;
|
|
342871
|
+
for (let i9 = 0; i9 < needle.length; i9++) {
|
|
342872
|
+
if (haystack[offset2 + i9] !== needle[i9]) return false;
|
|
342873
|
+
}
|
|
342874
|
+
return true;
|
|
342875
|
+
}
|
|
342876
|
+
function overlayRegions(currentGenLines, locInCurrentGen, workingTreeLines, locInWorkingTree) {
|
|
342877
|
+
if (locInCurrentGen.length !== locInWorkingTree.length) {
|
|
342878
|
+
return null;
|
|
342879
|
+
}
|
|
342880
|
+
const out = [];
|
|
342881
|
+
let cursor = 0;
|
|
342882
|
+
for (let i9 = 0; i9 < locInCurrentGen.length; i9++) {
|
|
342883
|
+
const cg = locInCurrentGen[i9];
|
|
342884
|
+
const wt10 = locInWorkingTree[i9];
|
|
342885
|
+
if (cg.oursOffset > cursor) {
|
|
342886
|
+
out.push(...currentGenLines.slice(cursor, cg.oursOffset));
|
|
342887
|
+
}
|
|
342888
|
+
out.push(...workingTreeLines.slice(wt10.oursOffset, wt10.oursOffset + wt10.oursSpan));
|
|
342889
|
+
cursor = cg.oursOffset + cg.oursSpan;
|
|
342890
|
+
}
|
|
342891
|
+
if (cursor < currentGenLines.length) {
|
|
342892
|
+
out.push(...currentGenLines.slice(cursor));
|
|
342893
|
+
}
|
|
342894
|
+
return out.join("\n");
|
|
342895
|
+
}
|
|
342896
|
+
async function unifiedDiff(beforeContent, afterContent, filePath) {
|
|
342897
|
+
if (beforeContent === afterContent) return "";
|
|
342898
|
+
const tmp18 = await (0, import_promises210.mkdtemp)((0, import_node_path310.join)((0, import_node_os22.tmpdir)(), "fern-replay-pp-"));
|
|
342899
|
+
try {
|
|
342900
|
+
const beforePath = (0, import_node_path310.join)(tmp18, "before");
|
|
342901
|
+
const afterPath = (0, import_node_path310.join)(tmp18, "after");
|
|
342902
|
+
await (0, import_promises210.writeFile)(beforePath, beforeContent, "utf-8");
|
|
342903
|
+
await (0, import_promises210.writeFile)(afterPath, afterContent, "utf-8");
|
|
342904
|
+
const raw = await runGitDiffNoIndex(beforePath, afterPath);
|
|
342905
|
+
if (!raw.trim()) return "";
|
|
342906
|
+
return rewriteDiffHeaders(raw, filePath);
|
|
342907
|
+
} finally {
|
|
342908
|
+
await (0, import_promises210.rm)(tmp18, { recursive: true, force: true });
|
|
342909
|
+
}
|
|
342910
|
+
}
|
|
342911
|
+
function runGitDiffNoIndex(beforePath, afterPath) {
|
|
342912
|
+
return new Promise((resolve22, reject) => {
|
|
342913
|
+
const proc2 = (0, import_node_child_process7.spawn)("git", ["diff", "--no-index", "--", beforePath, afterPath]);
|
|
342914
|
+
let stdout = "";
|
|
342915
|
+
let stderr = "";
|
|
342916
|
+
proc2.stdout.on("data", (d5) => stdout += d5.toString());
|
|
342917
|
+
proc2.stderr.on("data", (d5) => stderr += d5.toString());
|
|
342918
|
+
proc2.on("close", (code5) => {
|
|
342919
|
+
if (code5 === 0 || code5 === 1) resolve22(stdout);
|
|
342920
|
+
else reject(new Error(`git diff --no-index failed (code ${code5}): ${stderr}`));
|
|
342921
|
+
});
|
|
342922
|
+
});
|
|
342923
|
+
}
|
|
342924
|
+
function rewriteDiffHeaders(raw, filePath) {
|
|
342925
|
+
const lines = raw.split("\n");
|
|
342926
|
+
const out = [];
|
|
342927
|
+
let seenFirstHeader = false;
|
|
342928
|
+
for (const line of lines) {
|
|
342929
|
+
if (line.startsWith("diff --git ")) {
|
|
342930
|
+
out.push(`diff --git a/${filePath} b/${filePath}`);
|
|
342931
|
+
seenFirstHeader = true;
|
|
342932
|
+
continue;
|
|
342933
|
+
}
|
|
342934
|
+
if (!seenFirstHeader) {
|
|
342935
|
+
continue;
|
|
342936
|
+
}
|
|
342937
|
+
if (line.startsWith("--- ")) {
|
|
342938
|
+
out.push(`--- a/${filePath}`);
|
|
342939
|
+
continue;
|
|
342940
|
+
}
|
|
342941
|
+
if (line.startsWith("+++ ")) {
|
|
342942
|
+
out.push(`+++ b/${filePath}`);
|
|
342943
|
+
continue;
|
|
342944
|
+
}
|
|
342945
|
+
out.push(line);
|
|
342946
|
+
}
|
|
342947
|
+
return out.join("\n");
|
|
342948
|
+
}
|
|
342949
|
+
function synthesizeNewFileDiff(file4, content5) {
|
|
342950
|
+
const lines = content5.split("\n");
|
|
342951
|
+
const hasTrailingNewline = content5.endsWith("\n");
|
|
342952
|
+
const bodyLines = hasTrailingNewline ? lines.slice(0, -1) : lines;
|
|
342953
|
+
const header = [
|
|
342954
|
+
`diff --git a/${file4} b/${file4}`,
|
|
342955
|
+
"new file mode 100644",
|
|
342956
|
+
"--- /dev/null",
|
|
342957
|
+
`+++ b/${file4}`,
|
|
342958
|
+
`@@ -0,0 +1,${bodyLines.length} @@`
|
|
342959
|
+
].join("\n");
|
|
342960
|
+
const body = bodyLines.map((l9) => `+${l9}`).join("\n");
|
|
342961
|
+
const trailer = hasTrailingNewline ? "" : "\n\";
|
|
342962
|
+
return header + "\n" + body + trailer + "\n";
|
|
342963
|
+
}
|
|
342964
|
+
function synthesizeDeleteFileDiff(file4, content5) {
|
|
342965
|
+
const lines = content5.split("\n");
|
|
342966
|
+
const hasTrailingNewline = content5.endsWith("\n");
|
|
342967
|
+
const bodyLines = hasTrailingNewline ? lines.slice(0, -1) : lines;
|
|
342968
|
+
const header = [
|
|
342969
|
+
`diff --git a/${file4} b/${file4}`,
|
|
342970
|
+
"deleted file mode 100644",
|
|
342971
|
+
`--- a/${file4}`,
|
|
342972
|
+
"+++ /dev/null",
|
|
342973
|
+
`@@ -1,${bodyLines.length} +0,0 @@`
|
|
342974
|
+
].join("\n");
|
|
342975
|
+
const body = bodyLines.map((l9) => `-${l9}`).join("\n");
|
|
342976
|
+
const trailer = hasTrailingNewline ? "" : "\n\";
|
|
342977
|
+
return header + "\n" + body + trailer + "\n";
|
|
342978
|
+
}
|
|
342542
342979
|
var ReplayService = class {
|
|
342543
342980
|
git;
|
|
342544
342981
|
detector;
|
|
@@ -342932,6 +343369,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
342932
343369
|
if (newPatches.length > 0) {
|
|
342933
343370
|
results = await this.applicator.applyPatches(newPatches);
|
|
342934
343371
|
this._lastApplyResults = results;
|
|
343372
|
+
this.recordDroppedContextLineWarnings(results);
|
|
342935
343373
|
this.revertConflictingFiles(results);
|
|
342936
343374
|
for (const result of results) {
|
|
342937
343375
|
if (result.status === "conflict") {
|
|
@@ -342950,7 +343388,8 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
342950
343388
|
if (newPatches.length > 0) {
|
|
342951
343389
|
if (!options2?.stageOnly) {
|
|
342952
343390
|
const appliedCount = results.filter((r5) => r5.status === "applied").length;
|
|
342953
|
-
|
|
343391
|
+
const buckets = computeCommitBuckets(results, rebaseCounts.absorbedPatchIds, newPatches);
|
|
343392
|
+
await this.committer.commitReplay(appliedCount, newPatches, void 0, { buckets });
|
|
342954
343393
|
} else {
|
|
342955
343394
|
await this.committer.stageAll();
|
|
342956
343395
|
}
|
|
@@ -343013,12 +343452,21 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343013
343452
|
(p14) => preRebasePatchIds.has(p14.id) && !postRebasePatchIds.has(p14.id)
|
|
343014
343453
|
);
|
|
343015
343454
|
existingPatches = this.lockManager.getPatches();
|
|
343016
|
-
const seenHashes = /* @__PURE__ */ new
|
|
343455
|
+
const seenHashes = /* @__PURE__ */ new Map();
|
|
343017
343456
|
for (const p14 of existingPatches) {
|
|
343018
|
-
|
|
343019
|
-
|
|
343457
|
+
const priorOrigin = seenHashes.get(p14.content_hash);
|
|
343458
|
+
if (priorOrigin !== void 0) {
|
|
343459
|
+
const provenanceMissing = !priorOrigin || !p14.original_commit;
|
|
343460
|
+
const sameOrigin = priorOrigin === p14.original_commit;
|
|
343461
|
+
if (sameOrigin && !provenanceMissing) {
|
|
343462
|
+
this.lockManager.removePatch(p14.id);
|
|
343463
|
+
} else {
|
|
343464
|
+
this.warnings.push(
|
|
343465
|
+
`Preserving patch ${p14.id} (commit ${(p14.original_commit || "<missing>").slice(0, 7)}): shares content_hash with a prior patch ${provenanceMissing ? "(provenance missing on at least one side)" : "from a different source commit"}. Both retained to avoid silent customization loss (FER-9983).`
|
|
343466
|
+
);
|
|
343467
|
+
}
|
|
343020
343468
|
} else {
|
|
343021
|
-
seenHashes.
|
|
343469
|
+
seenHashes.set(p14.content_hash, p14.original_commit);
|
|
343022
343470
|
}
|
|
343023
343471
|
}
|
|
343024
343472
|
existingPatches = this.lockManager.getPatches();
|
|
@@ -343079,6 +343527,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343079
343527
|
const genSha = prep._prepared.genSha;
|
|
343080
343528
|
const results = await this.applicator.applyPatches(allPatches);
|
|
343081
343529
|
this._lastApplyResults = results;
|
|
343530
|
+
this.recordDroppedContextLineWarnings(results);
|
|
343082
343531
|
this.revertConflictingFiles(results);
|
|
343083
343532
|
for (const result of results) {
|
|
343084
343533
|
if (result.status === "conflict") {
|
|
@@ -343106,7 +343555,8 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343106
343555
|
await this.committer.stageAll();
|
|
343107
343556
|
} else {
|
|
343108
343557
|
const appliedCount = results.filter((r5) => r5.status === "applied").length;
|
|
343109
|
-
|
|
343558
|
+
const buckets = computeCommitBuckets(results, rebaseCounts.absorbedPatchIds, allPatches);
|
|
343559
|
+
await this.committer.commitReplay(appliedCount, allPatches, void 0, { buckets });
|
|
343110
343560
|
}
|
|
343111
343561
|
const warnings = [...detectorWarnings, ...this.warnings];
|
|
343112
343562
|
return this.buildReport(
|
|
@@ -343130,7 +343580,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343130
343580
|
let repointed = 0;
|
|
343131
343581
|
let contentRebased = 0;
|
|
343132
343582
|
let keptAsUserOwned = 0;
|
|
343133
|
-
const seenContentHashes = /* @__PURE__ */ new
|
|
343583
|
+
const seenContentHashes = /* @__PURE__ */ new Map();
|
|
343134
343584
|
const absorbedPatchIds = /* @__PURE__ */ new Set();
|
|
343135
343585
|
const fernignorePatterns = this.readFernignorePatterns();
|
|
343136
343586
|
for (const result of results) {
|
|
@@ -343144,6 +343594,23 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343144
343594
|
}
|
|
343145
343595
|
}
|
|
343146
343596
|
}
|
|
343597
|
+
const conflictedFilePaths = /* @__PURE__ */ new Set();
|
|
343598
|
+
for (const r5 of results) {
|
|
343599
|
+
if (r5.status !== "conflict") continue;
|
|
343600
|
+
if (r5.fileResults) {
|
|
343601
|
+
for (const fr9 of r5.fileResults) {
|
|
343602
|
+
if (fr9.status !== "conflict") continue;
|
|
343603
|
+
conflictedFilePaths.add(fr9.file);
|
|
343604
|
+
if (r5.resolvedFiles) {
|
|
343605
|
+
for (const [orig, resolved] of Object.entries(r5.resolvedFiles)) {
|
|
343606
|
+
if (resolved === fr9.file) conflictedFilePaths.add(orig);
|
|
343607
|
+
}
|
|
343608
|
+
}
|
|
343609
|
+
}
|
|
343610
|
+
} else {
|
|
343611
|
+
for (const f5 of r5.patch.files) conflictedFilePaths.add(f5);
|
|
343612
|
+
}
|
|
343613
|
+
}
|
|
343147
343614
|
for (const result of results) {
|
|
343148
343615
|
if (result.status === "conflict" && result.fileResults) {
|
|
343149
343616
|
await this.trimAbsorbedFiles(result, currentGenSha);
|
|
@@ -343211,6 +343678,18 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343211
343678
|
keptAsUserOwned++;
|
|
343212
343679
|
continue;
|
|
343213
343680
|
}
|
|
343681
|
+
const overlapsConflicted = patch5.files.some((f5) => conflictedFilePaths.has(f5));
|
|
343682
|
+
if (overlapsConflicted) {
|
|
343683
|
+
patch5.status = "unresolved";
|
|
343684
|
+
try {
|
|
343685
|
+
this.lockManager.updatePatch(patch5.id, { status: "unresolved" });
|
|
343686
|
+
} catch {
|
|
343687
|
+
}
|
|
343688
|
+
this.warnings.push(
|
|
343689
|
+
`Patch ${patch5.id} (${patch5.original_message}) was preserved as unresolved because an earlier patch conflicted on the same file(s) (${patch5.files.join(", ")}). Run \`fern replay resolve\` to apply this customization manually.`
|
|
343690
|
+
);
|
|
343691
|
+
continue;
|
|
343692
|
+
}
|
|
343214
343693
|
this.lockManager.removePatch(patch5.id);
|
|
343215
343694
|
absorbedPatchIds.add(patch5.id);
|
|
343216
343695
|
absorbed++;
|
|
@@ -343226,13 +343705,22 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343226
343705
|
continue;
|
|
343227
343706
|
}
|
|
343228
343707
|
const newContentHash = this.detector.computeContentHash(diff2);
|
|
343229
|
-
|
|
343230
|
-
|
|
343231
|
-
|
|
343232
|
-
|
|
343233
|
-
|
|
343708
|
+
const priorOrigin = seenContentHashes.get(newContentHash);
|
|
343709
|
+
if (priorOrigin !== void 0) {
|
|
343710
|
+
const provenanceMissing = !priorOrigin || !patch5.original_commit;
|
|
343711
|
+
const sameOrigin = priorOrigin === patch5.original_commit;
|
|
343712
|
+
if (sameOrigin && !provenanceMissing) {
|
|
343713
|
+
this.lockManager.removePatch(patch5.id);
|
|
343714
|
+
absorbedPatchIds.add(patch5.id);
|
|
343715
|
+
absorbed++;
|
|
343716
|
+
continue;
|
|
343717
|
+
}
|
|
343718
|
+
this.warnings.push(
|
|
343719
|
+
`Preserving patch ${patch5.id} (commit ${(patch5.original_commit || "<missing>").slice(0, 7)}): shares content_hash with a prior patch ${provenanceMissing ? "(provenance missing on at least one side)" : "from a different source commit"}. Both retained to avoid silent customization loss (FER-9983).`
|
|
343720
|
+
);
|
|
343721
|
+
} else {
|
|
343722
|
+
seenContentHashes.set(newContentHash, patch5.original_commit);
|
|
343234
343723
|
}
|
|
343235
|
-
seenContentHashes.add(newContentHash);
|
|
343236
343724
|
patch5.base_generation = currentGenSha;
|
|
343237
343725
|
patch5.patch_content = diff2;
|
|
343238
343726
|
patch5.content_hash = newContentHash;
|
|
@@ -343408,8 +343896,14 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343408
343896
|
}
|
|
343409
343897
|
if (patch5.base_generation === currentGen) {
|
|
343410
343898
|
try {
|
|
343411
|
-
const
|
|
343412
|
-
|
|
343899
|
+
const ppResult = await computePerPatchDiffWithFallback(
|
|
343900
|
+
patch5,
|
|
343901
|
+
(f5) => this.git.showFile(currentGen, f5),
|
|
343902
|
+
(f5) => this.git.showFile("HEAD", f5),
|
|
343903
|
+
(files) => this.git.exec(["diff", currentGen, "HEAD", "--", ...files]).catch(() => null)
|
|
343904
|
+
);
|
|
343905
|
+
if (ppResult === null) continue;
|
|
343906
|
+
const diff2 = ppResult.diff;
|
|
343413
343907
|
if (!diff2.trim()) {
|
|
343414
343908
|
if (await allPatchFilesUserOwned(patch5)) continue;
|
|
343415
343909
|
this.lockManager.removePatch(patch5.id);
|
|
@@ -343451,8 +343945,14 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343451
343945
|
try {
|
|
343452
343946
|
const markerFiles = await this.git.exec(["grep", "-l", "<<<<<<< Generated", "HEAD", "--", ...patch5.files]).catch(() => "");
|
|
343453
343947
|
if (markerFiles.trim()) continue;
|
|
343454
|
-
const
|
|
343455
|
-
|
|
343948
|
+
const ppResult = await computePerPatchDiffWithFallback(
|
|
343949
|
+
patch5,
|
|
343950
|
+
(f5) => this.git.showFile(currentGen, f5),
|
|
343951
|
+
(f5) => this.git.showFile("HEAD", f5),
|
|
343952
|
+
(files) => this.git.exec(["diff", currentGen, "HEAD", "--", ...files]).catch(() => null)
|
|
343953
|
+
);
|
|
343954
|
+
if (ppResult === null) continue;
|
|
343955
|
+
const diff2 = ppResult.diff;
|
|
343456
343956
|
if (!diff2.trim()) {
|
|
343457
343957
|
if (await allPatchFilesUserOwned(patch5)) {
|
|
343458
343958
|
try {
|
|
@@ -343477,6 +343977,32 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343477
343977
|
}
|
|
343478
343978
|
return { conflictResolved, conflictAbsorbed, contentRefreshed };
|
|
343479
343979
|
}
|
|
343980
|
+
/**
|
|
343981
|
+
* After applyPatches(), surface a warning for each (patch × file) where
|
|
343982
|
+
* diff3 silently dropped lines that were unchanged context in the
|
|
343983
|
+
* customer's THEIRS. The deletion stays on disk (correct diff3 outcome),
|
|
343984
|
+
* but the customer must learn so they can re-add the lines if they
|
|
343985
|
+
* relied on them. This is the "surface or preserve, never silently drop"
|
|
343986
|
+
* contract for legitimate-deletion cases.
|
|
343987
|
+
*/
|
|
343988
|
+
recordDroppedContextLineWarnings(results) {
|
|
343989
|
+
const PREVIEW_COUNT = 5;
|
|
343990
|
+
for (const result of results) {
|
|
343991
|
+
if (!result.fileResults) continue;
|
|
343992
|
+
for (const fr9 of result.fileResults) {
|
|
343993
|
+
const dropped = fr9.droppedContextLines;
|
|
343994
|
+
if (!dropped || dropped.length === 0) continue;
|
|
343995
|
+
const preview = dropped.slice(0, PREVIEW_COUNT).map((line) => ` ${line}`).join("\n");
|
|
343996
|
+
const more = dropped.length > PREVIEW_COUNT ? `
|
|
343997
|
+
... and ${dropped.length - PREVIEW_COUNT} more` : "";
|
|
343998
|
+
this.warnings.push(
|
|
343999
|
+
`${fr9.file}: ${dropped.length} line(s) that appeared as unchanged context in your customization were deleted by the new generator output. The merge followed the deletion. First lines deleted:
|
|
344000
|
+
${preview}${more}
|
|
344001
|
+
If you want to keep these lines, restore them in a follow-up commit and re-run replay; otherwise this warning can be safely ignored.`
|
|
344002
|
+
);
|
|
344003
|
+
}
|
|
344004
|
+
}
|
|
344005
|
+
}
|
|
343480
344006
|
/**
|
|
343481
344007
|
* After applyPatches(), strip conflict markers from conflicting files
|
|
343482
344008
|
* so only clean content is committed. Keeps the Generated (OURS) side.
|
|
@@ -343486,7 +344012,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343486
344012
|
if (result.status !== "conflict" || !result.fileResults) continue;
|
|
343487
344013
|
for (const fileResult of result.fileResults) {
|
|
343488
344014
|
if (fileResult.status !== "conflict") continue;
|
|
343489
|
-
const filePath = (0,
|
|
344015
|
+
const filePath = (0, import_node_path43.join)(this.outputDir, fileResult.file);
|
|
343490
344016
|
try {
|
|
343491
344017
|
const content5 = (0, import_node_fs210.readFileSync)(filePath, "utf-8");
|
|
343492
344018
|
const stripped = stripConflictMarkers(content5);
|
|
@@ -343516,7 +344042,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343516
344042
|
}
|
|
343517
344043
|
}
|
|
343518
344044
|
readFernignorePatterns() {
|
|
343519
|
-
const fernignorePath = (0,
|
|
344045
|
+
const fernignorePath = (0, import_node_path43.join)(this.outputDir, ".fernignore");
|
|
343520
344046
|
if (!(0, import_node_fs210.existsSync)(fernignorePath)) return [];
|
|
343521
344047
|
return (0, import_node_fs210.readFileSync)(fernignorePath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
343522
344048
|
}
|
|
@@ -343534,6 +344060,9 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343534
344060
|
};
|
|
343535
344061
|
}).filter((d5) => d5.files.length > 0);
|
|
343536
344062
|
const partialCount = conflictDetails.filter((d5) => d5.cleanFiles && d5.cleanFiles.length > 0).length;
|
|
344063
|
+
const unresolvedResults = results.filter(
|
|
344064
|
+
(r5) => r5.status === "conflict" || r5.patch.status === "unresolved"
|
|
344065
|
+
);
|
|
343537
344066
|
return {
|
|
343538
344067
|
flow: flow4,
|
|
343539
344068
|
patchesDetected: patches.length,
|
|
@@ -343550,7 +344079,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343550
344079
|
patchesRefreshed: preRebaseCounts && preRebaseCounts.contentRefreshed > 0 ? preRebaseCounts.contentRefreshed : void 0,
|
|
343551
344080
|
conflicts: conflictResults.flatMap((r5) => r5.fileResults?.filter((f5) => f5.status === "conflict") ?? []),
|
|
343552
344081
|
conflictDetails: conflictDetails.length > 0 ? conflictDetails : void 0,
|
|
343553
|
-
unresolvedPatches:
|
|
344082
|
+
unresolvedPatches: unresolvedResults.length > 0 ? unresolvedResults.map((r5) => ({
|
|
343554
344083
|
patchId: r5.patch.id,
|
|
343555
344084
|
patchMessage: r5.patch.original_message,
|
|
343556
344085
|
files: r5.patch.files,
|
|
@@ -343561,9 +344090,36 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343561
344090
|
};
|
|
343562
344091
|
}
|
|
343563
344092
|
};
|
|
344093
|
+
function computeCommitBuckets(results, absorbedPatchIds, patchesPassedToCommit) {
|
|
344094
|
+
const resultByPatchId = /* @__PURE__ */ new Map();
|
|
344095
|
+
for (const r5 of results) resultByPatchId.set(r5.patch.id, r5);
|
|
344096
|
+
const applied = [];
|
|
344097
|
+
const unresolved = [];
|
|
344098
|
+
const absorbed = [];
|
|
344099
|
+
for (const patch5 of patchesPassedToCommit) {
|
|
344100
|
+
if (absorbedPatchIds.has(patch5.id)) {
|
|
344101
|
+
absorbed.push(patch5);
|
|
344102
|
+
continue;
|
|
344103
|
+
}
|
|
344104
|
+
const r5 = resultByPatchId.get(patch5.id);
|
|
344105
|
+
if (!r5) {
|
|
344106
|
+
applied.push(patch5);
|
|
344107
|
+
continue;
|
|
344108
|
+
}
|
|
344109
|
+
if (r5.status === "conflict" || patch5.status === "unresolved") {
|
|
344110
|
+
unresolved.push(patch5);
|
|
344111
|
+
continue;
|
|
344112
|
+
}
|
|
344113
|
+
if (r5.status === "applied") {
|
|
344114
|
+
applied.push(patch5);
|
|
344115
|
+
continue;
|
|
344116
|
+
}
|
|
344117
|
+
}
|
|
344118
|
+
return { applied, unresolved, absorbed };
|
|
344119
|
+
}
|
|
343564
344120
|
var import_node_crypto22 = require("crypto");
|
|
343565
344121
|
var import_node_fs33 = require("fs");
|
|
343566
|
-
var
|
|
344122
|
+
var import_node_path52 = require("path");
|
|
343567
344123
|
var import_minimatch32 = require_commonjs3();
|
|
343568
344124
|
var import_yaml22 = require_dist9();
|
|
343569
344125
|
var FernignoreMigrator = class {
|
|
@@ -343576,10 +344132,10 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343576
344132
|
this.outputDir = outputDir;
|
|
343577
344133
|
}
|
|
343578
344134
|
fernignoreExists() {
|
|
343579
|
-
return (0, import_node_fs33.existsSync)((0,
|
|
344135
|
+
return (0, import_node_fs33.existsSync)((0, import_node_path52.join)(this.outputDir, ".fernignore"));
|
|
343580
344136
|
}
|
|
343581
344137
|
readFernignorePatterns() {
|
|
343582
|
-
const fernignorePath = (0,
|
|
344138
|
+
const fernignorePath = (0, import_node_path52.join)(this.outputDir, ".fernignore");
|
|
343583
344139
|
if (!(0, import_node_fs33.existsSync)(fernignorePath)) {
|
|
343584
344140
|
return [];
|
|
343585
344141
|
}
|
|
@@ -343639,7 +344195,16 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343639
344195
|
original_author: "Fern Replay <replay@buildwithfern.com>",
|
|
343640
344196
|
base_generation: currentGen.commit_sha,
|
|
343641
344197
|
files: patchFiles,
|
|
343642
|
-
patch_content: patchContent
|
|
344198
|
+
patch_content: patchContent,
|
|
344199
|
+
// Synthetic patches captured from .fernignore-protected files
|
|
344200
|
+
// are user-owned by definition — the customer's content
|
|
344201
|
+
// diverges from the generator's pristine output, and the
|
|
344202
|
+
// generator never produced this divergent content. Without
|
|
344203
|
+
// this flag, removing the file from .fernignore later (so
|
|
344204
|
+
// it leaves the protection check in `isFileUserOwned`) would
|
|
344205
|
+
// expose the patch to absorption on the next regen,
|
|
344206
|
+
// silently losing the customization.
|
|
344207
|
+
user_owned: true
|
|
343643
344208
|
});
|
|
343644
344209
|
trackedByBoth.push({
|
|
343645
344210
|
file: pattern,
|
|
@@ -343672,7 +344237,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343672
344237
|
return results;
|
|
343673
344238
|
}
|
|
343674
344239
|
readFileContent(filePath) {
|
|
343675
|
-
const fullPath = (0,
|
|
344240
|
+
const fullPath = (0, import_node_path52.join)(this.outputDir, filePath);
|
|
343676
344241
|
if (!(0, import_node_fs33.existsSync)(fullPath)) {
|
|
343677
344242
|
return null;
|
|
343678
344243
|
}
|
|
@@ -343737,7 +344302,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343737
344302
|
};
|
|
343738
344303
|
}
|
|
343739
344304
|
movePatternsToReplayYml(patterns) {
|
|
343740
|
-
const replayYmlPath = (0,
|
|
344305
|
+
const replayYmlPath = (0, import_node_path52.join)(this.outputDir, ".fern", "replay.yml");
|
|
343741
344306
|
let config5 = {};
|
|
343742
344307
|
if ((0, import_node_fs33.existsSync)(replayYmlPath)) {
|
|
343743
344308
|
const content5 = (0, import_node_fs33.readFileSync)(replayYmlPath, "utf-8");
|
|
@@ -343746,7 +344311,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343746
344311
|
const existing = config5.exclude ?? [];
|
|
343747
344312
|
const merged = [.../* @__PURE__ */ new Set([...existing, ...patterns])];
|
|
343748
344313
|
config5.exclude = merged;
|
|
343749
|
-
const dir = (0,
|
|
344314
|
+
const dir = (0, import_node_path52.dirname)(replayYmlPath);
|
|
343750
344315
|
if (!(0, import_node_fs33.existsSync)(dir)) {
|
|
343751
344316
|
(0, import_node_fs33.mkdirSync)(dir, { recursive: true });
|
|
343752
344317
|
}
|
|
@@ -343755,7 +344320,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343755
344320
|
};
|
|
343756
344321
|
var import_node_crypto32 = require("crypto");
|
|
343757
344322
|
var import_node_fs42 = require("fs");
|
|
343758
|
-
var
|
|
344323
|
+
var import_node_path62 = require("path");
|
|
343759
344324
|
init_GitClient();
|
|
343760
344325
|
async function bootstrap(outputDir, options2) {
|
|
343761
344326
|
const git = new GitClient(outputDir);
|
|
@@ -343922,7 +344487,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343922
344487
|
}
|
|
343923
344488
|
var REPLAY_FERNIGNORE_ENTRIES = [".fern/replay.lock", ".fern/replay.yml", ".gitattributes"];
|
|
343924
344489
|
function ensureFernignoreEntries(outputDir) {
|
|
343925
|
-
const fernignorePath = (0,
|
|
344490
|
+
const fernignorePath = (0, import_node_path62.join)(outputDir, ".fernignore");
|
|
343926
344491
|
let content5 = "";
|
|
343927
344492
|
if ((0, import_node_fs42.existsSync)(fernignorePath)) {
|
|
343928
344493
|
content5 = (0, import_node_fs42.readFileSync)(fernignorePath, "utf-8");
|
|
@@ -343946,7 +344511,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
343946
344511
|
}
|
|
343947
344512
|
var GITATTRIBUTES_ENTRIES = [".fern/replay.lock linguist-generated=true"];
|
|
343948
344513
|
function ensureGitattributesEntries(outputDir) {
|
|
343949
|
-
const gitattributesPath = (0,
|
|
344514
|
+
const gitattributesPath = (0, import_node_path62.join)(outputDir, ".gitattributes");
|
|
343950
344515
|
let content5 = "";
|
|
343951
344516
|
if ((0, import_node_fs42.existsSync)(gitattributesPath)) {
|
|
343952
344517
|
content5 = (0, import_node_fs42.readFileSync)(gitattributesPath, "utf-8");
|
|
@@ -344138,6 +344703,8 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
344138
344703
|
nothingToReset: false
|
|
344139
344704
|
};
|
|
344140
344705
|
}
|
|
344706
|
+
var import_promises310 = require("fs/promises");
|
|
344707
|
+
var import_node_path72 = require("path");
|
|
344141
344708
|
init_GitClient();
|
|
344142
344709
|
async function resolve15(outputDir, options2) {
|
|
344143
344710
|
const lockManager = new LockfileManager(outputDir);
|
|
@@ -344153,7 +344720,7 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
344153
344720
|
const resolvingPatches = lockManager.getResolvingPatches();
|
|
344154
344721
|
if (unresolvedPatches.length > 0) {
|
|
344155
344722
|
const applicator = new ReplayApplicator(git, lockManager, outputDir);
|
|
344156
|
-
await applicator.applyPatches(unresolvedPatches);
|
|
344723
|
+
await applicator.applyPatches(unresolvedPatches, { applyMode: "resolve" });
|
|
344157
344724
|
const markerFiles = await findConflictMarkerFiles(git);
|
|
344158
344725
|
if (markerFiles.length > 0) {
|
|
344159
344726
|
for (const patch5 of unresolvedPatches) {
|
|
@@ -344179,20 +344746,38 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
344179
344746
|
if (patchesToCommit.length > 0) {
|
|
344180
344747
|
const currentGen = lock.current_generation;
|
|
344181
344748
|
const detector = new ReplayDetector(git, lockManager, outputDir);
|
|
344749
|
+
const warnings = [];
|
|
344182
344750
|
let patchesResolved = 0;
|
|
344183
344751
|
for (const patch5 of patchesToCommit) {
|
|
344184
|
-
const
|
|
344185
|
-
|
|
344752
|
+
const result = await computePerPatchDiffWithFallback(
|
|
344753
|
+
patch5,
|
|
344754
|
+
(f5) => git.showFile(currentGen, f5),
|
|
344755
|
+
(f5) => (0, import_promises310.readFile)((0, import_node_path72.join)(outputDir, f5), "utf-8").catch(() => null),
|
|
344756
|
+
(files) => git.exec(["diff", currentGen, "--", ...files]).catch(() => null)
|
|
344757
|
+
);
|
|
344758
|
+
if (result === null) {
|
|
344759
|
+
warnings.push(
|
|
344760
|
+
`Patch ${patch5.id}: cumulative-diff fallback failed for unlocatable files; preserving patch unchanged`
|
|
344761
|
+
);
|
|
344762
|
+
continue;
|
|
344763
|
+
}
|
|
344764
|
+
const diff2 = result.diff;
|
|
344765
|
+
if (!diff2.trim()) {
|
|
344186
344766
|
lockManager.removePatch(patch5.id);
|
|
344187
344767
|
continue;
|
|
344188
344768
|
}
|
|
344769
|
+
if (result.hadFallback) {
|
|
344770
|
+
warnings.push(
|
|
344771
|
+
`Patch ${patch5.id}: ${result.fallbackFiles.join(", ")} could not be anchored; cumulative-diff fallback applied for those files`
|
|
344772
|
+
);
|
|
344773
|
+
}
|
|
344189
344774
|
const newContentHash = detector.computeContentHash(diff2);
|
|
344190
|
-
const changedFiles = await getChangedFiles(git, currentGen, patch5.files);
|
|
344775
|
+
const changedFiles = result.hadFallback ? await getChangedFiles(git, currentGen, patch5.files) : extractFilesFromDiff(diff2);
|
|
344191
344776
|
lockManager.markPatchResolved(patch5.id, {
|
|
344192
344777
|
patch_content: diff2,
|
|
344193
344778
|
content_hash: newContentHash,
|
|
344194
344779
|
base_generation: currentGen,
|
|
344195
|
-
files: changedFiles
|
|
344780
|
+
files: changedFiles.length > 0 ? changedFiles : patch5.files
|
|
344196
344781
|
});
|
|
344197
344782
|
patchesResolved++;
|
|
344198
344783
|
}
|
|
@@ -344208,7 +344793,8 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
344208
344793
|
success: true,
|
|
344209
344794
|
commitSha: commitSha2,
|
|
344210
344795
|
phase: "committed",
|
|
344211
|
-
patchesResolved
|
|
344796
|
+
patchesResolved,
|
|
344797
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
344212
344798
|
};
|
|
344213
344799
|
}
|
|
344214
344800
|
const committer = new ReplayCommitter(git, outputDir);
|
|
@@ -344226,6 +344812,24 @@ CLI Version: ${options2.cliVersion}`;
|
|
|
344226
344812
|
const changed = filesOutput.trim().split("\n").filter(Boolean).filter((f5) => !f5.startsWith(".fern/"));
|
|
344227
344813
|
return changed.length > 0 ? changed : files;
|
|
344228
344814
|
}
|
|
344815
|
+
function extractFilesFromDiff(unifiedDiff2) {
|
|
344816
|
+
const out = /* @__PURE__ */ new Set();
|
|
344817
|
+
const renameSources = /* @__PURE__ */ new Set();
|
|
344818
|
+
let currentTarget = null;
|
|
344819
|
+
for (const line of unifiedDiff2.split("\n")) {
|
|
344820
|
+
const headerMatch = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
|
|
344821
|
+
if (headerMatch) {
|
|
344822
|
+
currentTarget = headerMatch[2];
|
|
344823
|
+
if (!currentTarget.startsWith(".fern/")) out.add(currentTarget);
|
|
344824
|
+
continue;
|
|
344825
|
+
}
|
|
344826
|
+
if (currentTarget && line.startsWith("rename from ")) {
|
|
344827
|
+
renameSources.add(line.slice("rename from ".length));
|
|
344828
|
+
}
|
|
344829
|
+
}
|
|
344830
|
+
for (const src of renameSources) out.delete(src);
|
|
344831
|
+
return Array.from(out);
|
|
344832
|
+
}
|
|
344229
344833
|
function status(outputDir) {
|
|
344230
344834
|
const lockManager = new LockfileManager(outputDir);
|
|
344231
344835
|
if (!lockManager.exists()) {
|
|
@@ -621386,7 +621990,7 @@ var AccessTokenPosthogManager = class {
|
|
|
621386
621990
|
properties: {
|
|
621387
621991
|
...event,
|
|
621388
621992
|
...event.properties,
|
|
621389
|
-
version: "5.
|
|
621993
|
+
version: "5.1.0",
|
|
621390
621994
|
usingAccessToken: true
|
|
621391
621995
|
}
|
|
621392
621996
|
});
|
|
@@ -621440,7 +622044,7 @@ var UserPosthogManager = class {
|
|
|
621440
622044
|
distinctId: this.userId ?? await this.getPersistedDistinctId(),
|
|
621441
622045
|
event: "CLI",
|
|
621442
622046
|
properties: {
|
|
621443
|
-
version: "5.
|
|
622047
|
+
version: "5.1.0",
|
|
621444
622048
|
...event,
|
|
621445
622049
|
...event.properties,
|
|
621446
622050
|
usingAccessToken: false,
|
|
@@ -625100,6 +625704,133 @@ var TelemetryClient = class _TelemetryClient {
|
|
|
625100
625704
|
}
|
|
625101
625705
|
};
|
|
625102
625706
|
|
|
625707
|
+
// ../cli-v2/lib/ui/TtyAwareLogger.js
|
|
625708
|
+
var import_is_ci3 = __toESM(require_is_ci(), 1);
|
|
625709
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
625710
|
+
var SPINNER_INTERVAL_MS = 100;
|
|
625711
|
+
var TtyAwareLogger2 = class {
|
|
625712
|
+
isTTY;
|
|
625713
|
+
stdout;
|
|
625714
|
+
stderr;
|
|
625715
|
+
paintables = [];
|
|
625716
|
+
lastPaint = "";
|
|
625717
|
+
frameIndex = 0;
|
|
625718
|
+
interval;
|
|
625719
|
+
suspended = false;
|
|
625720
|
+
suspendBuffer = [];
|
|
625721
|
+
finished = false;
|
|
625722
|
+
constructor(stdout, stderr) {
|
|
625723
|
+
this.stdout = stdout;
|
|
625724
|
+
this.stderr = stderr;
|
|
625725
|
+
this.isTTY = stdout.isTTY === true && !import_is_ci3.default;
|
|
625726
|
+
}
|
|
625727
|
+
/**
|
|
625728
|
+
* Register a paintable. The first registration starts the repaint loop.
|
|
625729
|
+
*/
|
|
625730
|
+
register(paintable) {
|
|
625731
|
+
this.paintables.push(paintable);
|
|
625732
|
+
if (this.isTTY && this.interval == null) {
|
|
625733
|
+
this.stdout.write(ansi_escapes_default.cursorHide);
|
|
625734
|
+
this.repaint();
|
|
625735
|
+
this.interval = setInterval(() => this.repaint(), SPINNER_INTERVAL_MS);
|
|
625736
|
+
}
|
|
625737
|
+
}
|
|
625738
|
+
/**
|
|
625739
|
+
* Write content above the paint region.
|
|
625740
|
+
*
|
|
625741
|
+
* On non-TTY streams the content is written verbatim. On TTY streams the
|
|
625742
|
+
* paint region is cleared, the content is written, and the paint region
|
|
625743
|
+
* is redrawn in place.
|
|
625744
|
+
*/
|
|
625745
|
+
write(content5, { stderr = false } = {}) {
|
|
625746
|
+
const stream5 = stderr ? this.stderr : this.stdout;
|
|
625747
|
+
if (!this.isTTY) {
|
|
625748
|
+
stream5.write(content5);
|
|
625749
|
+
return;
|
|
625750
|
+
}
|
|
625751
|
+
if (this.suspended) {
|
|
625752
|
+
this.suspendBuffer.push({ content: content5, stderr });
|
|
625753
|
+
return;
|
|
625754
|
+
}
|
|
625755
|
+
this.stdout.write(this.clear());
|
|
625756
|
+
stream5.write(content5);
|
|
625757
|
+
this.stdout.write(this.paint());
|
|
625758
|
+
}
|
|
625759
|
+
/**
|
|
625760
|
+
* Pause the paint loop while `run` executes, buffering any `write()` calls,
|
|
625761
|
+
* then flush the buffer and resume. Useful for writing content that must
|
|
625762
|
+
* not be clobbered by a repaint (e.g. an inquirer prompt).
|
|
625763
|
+
*/
|
|
625764
|
+
async takeOverTerminal(run) {
|
|
625765
|
+
if (!this.isTTY) {
|
|
625766
|
+
await run();
|
|
625767
|
+
return;
|
|
625768
|
+
}
|
|
625769
|
+
this.stopLoop();
|
|
625770
|
+
this.stdout.write(this.clear());
|
|
625771
|
+
this.suspended = true;
|
|
625772
|
+
try {
|
|
625773
|
+
await run();
|
|
625774
|
+
} finally {
|
|
625775
|
+
this.suspended = false;
|
|
625776
|
+
for (const { content: content5, stderr: isStderr } of this.suspendBuffer) {
|
|
625777
|
+
const stream5 = isStderr ? this.stderr : this.stdout;
|
|
625778
|
+
stream5.write(content5);
|
|
625779
|
+
}
|
|
625780
|
+
this.suspendBuffer = [];
|
|
625781
|
+
if (this.paintables.length > 0) {
|
|
625782
|
+
this.stdout.write(ansi_escapes_default.cursorHide);
|
|
625783
|
+
this.repaint();
|
|
625784
|
+
this.interval = setInterval(() => this.repaint(), SPINNER_INTERVAL_MS);
|
|
625785
|
+
}
|
|
625786
|
+
}
|
|
625787
|
+
}
|
|
625788
|
+
/**
|
|
625789
|
+
* Stop the paint loop, perform one final repaint, and restore the cursor.
|
|
625790
|
+
* Idempotent.
|
|
625791
|
+
*/
|
|
625792
|
+
finish() {
|
|
625793
|
+
if (!this.isTTY || this.finished) {
|
|
625794
|
+
return;
|
|
625795
|
+
}
|
|
625796
|
+
this.finished = true;
|
|
625797
|
+
this.stopLoop();
|
|
625798
|
+
this.stdout.write(this.clear() + this.paint());
|
|
625799
|
+
this.stdout.write(ansi_escapes_default.cursorShow);
|
|
625800
|
+
}
|
|
625801
|
+
stopLoop() {
|
|
625802
|
+
if (this.interval != null) {
|
|
625803
|
+
clearInterval(this.interval);
|
|
625804
|
+
this.interval = void 0;
|
|
625805
|
+
}
|
|
625806
|
+
}
|
|
625807
|
+
repaint() {
|
|
625808
|
+
this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
|
|
625809
|
+
this.stdout.write(this.clear() + this.paint());
|
|
625810
|
+
}
|
|
625811
|
+
clear() {
|
|
625812
|
+
if (this.lastPaint.length === 0) {
|
|
625813
|
+
return "";
|
|
625814
|
+
}
|
|
625815
|
+
return ansi_escapes_default.eraseLines(this.lastPaint.split("\n").length);
|
|
625816
|
+
}
|
|
625817
|
+
paint() {
|
|
625818
|
+
if (this.paintables.length === 0) {
|
|
625819
|
+
this.lastPaint = "";
|
|
625820
|
+
return "";
|
|
625821
|
+
}
|
|
625822
|
+
const spinnerFrame = SPINNER_FRAMES[this.frameIndex] ?? SPINNER_FRAMES[0];
|
|
625823
|
+
const bodies = this.paintables.map((p14) => p14.paint(spinnerFrame)).filter((body) => body.length > 0);
|
|
625824
|
+
if (bodies.length === 0) {
|
|
625825
|
+
this.lastPaint = "";
|
|
625826
|
+
return "";
|
|
625827
|
+
}
|
|
625828
|
+
const paint = bodies.join("\n") + "\n";
|
|
625829
|
+
this.lastPaint = paint;
|
|
625830
|
+
return paint;
|
|
625831
|
+
}
|
|
625832
|
+
};
|
|
625833
|
+
|
|
625103
625834
|
// ../cli-v2/lib/api/config/converter/ApiDefinitionConverter.js
|
|
625104
625835
|
init_lib6();
|
|
625105
625836
|
var DEFAULT_API_NAME = "api";
|
|
@@ -626002,7 +626733,7 @@ var Context = class _Context {
|
|
|
626002
626733
|
tokenService;
|
|
626003
626734
|
ttyAwareLogger;
|
|
626004
626735
|
static async create({ stdout, stderr, cwd: cwd2, logLevel }) {
|
|
626005
|
-
const ttyAwareLogger = new
|
|
626736
|
+
const ttyAwareLogger = new TtyAwareLogger2(stdout, stderr);
|
|
626006
626737
|
const telemetry = await TelemetryClient.create({ isTTY: ttyAwareLogger.isTTY });
|
|
626007
626738
|
return new _Context({ stdout, stderr, cwd: cwd2, logLevel, ttyAwareLogger, telemetry });
|
|
626008
626739
|
}
|
|
@@ -626245,31 +626976,44 @@ ${source_default.dim(`Logs written to: ${this.logs.absoluteFilePath}`)}
|
|
|
626245
626976
|
this.ttyAwareLogger.finish();
|
|
626246
626977
|
}
|
|
626247
626978
|
log(level, ...parts) {
|
|
626248
|
-
this.
|
|
626249
|
-
{
|
|
626250
|
-
parts,
|
|
626251
|
-
level,
|
|
626252
|
-
time: /* @__PURE__ */ new Date()
|
|
626253
|
-
}
|
|
626254
|
-
]);
|
|
626979
|
+
this.writeLog(level, parts, { stderr: false });
|
|
626255
626980
|
}
|
|
626256
626981
|
logStderr(level, ...parts) {
|
|
626257
|
-
this.
|
|
626258
|
-
{
|
|
626259
|
-
parts,
|
|
626260
|
-
level,
|
|
626261
|
-
time: /* @__PURE__ */ new Date()
|
|
626262
|
-
}
|
|
626263
|
-
], { stderr: true });
|
|
626982
|
+
this.writeLog(level, parts, { stderr: true });
|
|
626264
626983
|
}
|
|
626265
|
-
|
|
626266
|
-
|
|
626267
|
-
|
|
626268
|
-
|
|
626269
|
-
|
|
626984
|
+
writeLog(level, parts, { stderr }) {
|
|
626985
|
+
if (LOG_LEVELS.indexOf(level) < LOG_LEVELS.indexOf(this.logLevel)) {
|
|
626986
|
+
return;
|
|
626987
|
+
}
|
|
626988
|
+
const content5 = formatLogContent({
|
|
626989
|
+
level,
|
|
626990
|
+
parts,
|
|
626991
|
+
includeDebugInfo: this.logLevel === LogLevel.Debug
|
|
626270
626992
|
});
|
|
626993
|
+
this.ttyAwareLogger.write(content5, { stderr });
|
|
626271
626994
|
}
|
|
626272
626995
|
};
|
|
626996
|
+
function formatLogContent({ level, parts, includeDebugInfo }) {
|
|
626997
|
+
let content5 = parts.join(" ");
|
|
626998
|
+
if (includeDebugInfo) {
|
|
626999
|
+
content5 = `${source_default.dim(`${getLogLevelPrefix(level)} ${(/* @__PURE__ */ new Date()).toISOString()} `)}${content5}`;
|
|
627000
|
+
}
|
|
627001
|
+
content5 += "\n";
|
|
627002
|
+
switch (level) {
|
|
627003
|
+
case LogLevel.Error:
|
|
627004
|
+
return source_default.red(content5);
|
|
627005
|
+
case LogLevel.Warn:
|
|
627006
|
+
return source_default.hex("FFA500")(content5);
|
|
627007
|
+
case LogLevel.Trace:
|
|
627008
|
+
case LogLevel.Debug:
|
|
627009
|
+
case LogLevel.Info:
|
|
627010
|
+
return content5;
|
|
627011
|
+
}
|
|
627012
|
+
}
|
|
627013
|
+
var LOG_LEVEL_PREFIX_WIDTH = Math.max(...LOG_LEVELS.map((l9) => l9.length));
|
|
627014
|
+
function getLogLevelPrefix(level) {
|
|
627015
|
+
return level.toUpperCase().padEnd(LOG_LEVEL_PREFIX_WIDTH);
|
|
627016
|
+
}
|
|
626273
627017
|
|
|
626274
627018
|
// ../cli-v2/lib/context/loadDotenvFile.js
|
|
626275
627019
|
var import_dotenv = __toESM(require_main2(), 1);
|
|
@@ -833175,7 +833919,7 @@ var LOCAL_STORAGE_FOLDER4 = ".fern-dev";
|
|
|
833175
833919
|
var LOGS_FOLDER_NAME = "logs";
|
|
833176
833920
|
var MAX_LOGS_DIR_SIZE_BYTES = 100 * 1024 * 1024;
|
|
833177
833921
|
function getCliSource() {
|
|
833178
|
-
const version7 = "5.
|
|
833922
|
+
const version7 = "5.1.0";
|
|
833179
833923
|
return `cli@${version7}`;
|
|
833180
833924
|
}
|
|
833181
833925
|
var DebugLogger = class {
|
|
@@ -845593,7 +846337,7 @@ var LegacyDocsPublisher = class {
|
|
|
845593
846337
|
previewId,
|
|
845594
846338
|
disableTemplates: void 0,
|
|
845595
846339
|
skipUpload,
|
|
845596
|
-
cliVersion: "5.
|
|
846340
|
+
cliVersion: "5.1.0",
|
|
845597
846341
|
loginCommand: "fern auth login"
|
|
845598
846342
|
});
|
|
845599
846343
|
if (taskContext.getResult() === TaskResult.Failure) {
|
|
@@ -845733,15 +846477,19 @@ var TaskGroup = class _TaskGroup {
|
|
|
845733
846477
|
/**
|
|
845734
846478
|
* Called by TtyAwareLogger to get the current display.
|
|
845735
846479
|
* This is the integration point with TtyAwareLogger.
|
|
846480
|
+
*
|
|
846481
|
+
* Renders the task list wrapped in a bordered frame so multiple concurrent
|
|
846482
|
+
* TaskGroups (e.g. one per API in a fan-out) read as separate boxes rather
|
|
846483
|
+
* than a single megaframe.
|
|
845736
846484
|
*/
|
|
845737
|
-
|
|
846485
|
+
paint(spinnerFrame) {
|
|
845738
846486
|
const lines = [];
|
|
845739
846487
|
for (const id2 of this.taskOrder) {
|
|
845740
846488
|
const task = this.tasks[id2];
|
|
845741
846489
|
if (task == null) {
|
|
845742
846490
|
continue;
|
|
845743
846491
|
}
|
|
845744
|
-
const line = this.formatTaskLine(task,
|
|
846492
|
+
const line = this.formatTaskLine(task, spinnerFrame);
|
|
845745
846493
|
if (line != null) {
|
|
845746
846494
|
lines.push(line);
|
|
845747
846495
|
}
|
|
@@ -845749,7 +846497,8 @@ var TaskGroup = class _TaskGroup {
|
|
|
845749
846497
|
if (lines.length === 0) {
|
|
845750
846498
|
return "";
|
|
845751
846499
|
}
|
|
845752
|
-
|
|
846500
|
+
const body = lines.join("\n");
|
|
846501
|
+
return ["\u250C\u2500", addPrefixToString({ content: body, prefix: "\u2502 ", includePrefixOnAllLines: true }), "\u2514\u2500"].join("\n");
|
|
845753
846502
|
}
|
|
845754
846503
|
/**
|
|
845755
846504
|
* Complete the task group and show summary.
|
|
@@ -845842,7 +846591,7 @@ var TaskGroup = class _TaskGroup {
|
|
|
845842
846591
|
}
|
|
845843
846592
|
const lines = [];
|
|
845844
846593
|
for (const stage of stages) {
|
|
845845
|
-
const icon = this.getStageIcon(stage.status
|
|
846594
|
+
const icon = this.getStageIcon(stage.status);
|
|
845846
846595
|
let line = `
|
|
845847
846596
|
${icon} ${this.getStageLabel(stage)}`;
|
|
845848
846597
|
if (stage.detail != null) {
|
|
@@ -845873,7 +846622,7 @@ var TaskGroup = class _TaskGroup {
|
|
|
845873
846622
|
* Uses a static arrow (▸) for running stages to avoid visual noise
|
|
845874
846623
|
* from having multiple spinners (the parent task already has a spinner).
|
|
845875
846624
|
*/
|
|
845876
|
-
getStageIcon(status
|
|
846625
|
+
getStageIcon(status) {
|
|
845877
846626
|
switch (status) {
|
|
845878
846627
|
case "pending":
|
|
845879
846628
|
return source_default.dim("\u25CB");
|
|
@@ -846002,7 +846751,7 @@ ${indentStr}${source_default.dim(`... ${hiddenCount} earlier logs hidden ...`)}$
|
|
|
846002
846751
|
return;
|
|
846003
846752
|
}
|
|
846004
846753
|
this.isRegistered = true;
|
|
846005
|
-
this.context.ttyAwareLogger.
|
|
846754
|
+
this.context.ttyAwareLogger.register(this);
|
|
846006
846755
|
}
|
|
846007
846756
|
};
|
|
846008
846757
|
|
|
@@ -919915,7 +920664,7 @@ var CliContext = class _CliContext {
|
|
|
919915
920664
|
if (false) {
|
|
919916
920665
|
this.logger.error("CLI_VERSION is not defined");
|
|
919917
920666
|
}
|
|
919918
|
-
return "5.
|
|
920667
|
+
return "5.1.0";
|
|
919919
920668
|
}
|
|
919920
920669
|
getCliName() {
|
|
919921
920670
|
if (false) {
|
|
@@ -938746,7 +939495,7 @@ init_lib6();
|
|
|
938746
939495
|
var import_cli_progress2 = __toESM(require_cli_progress(), 1);
|
|
938747
939496
|
var import_fs42 = require("fs");
|
|
938748
939497
|
var import_promises191 = require("fs/promises");
|
|
938749
|
-
var
|
|
939498
|
+
var import_is_ci4 = __toESM(require_is_ci(), 1);
|
|
938750
939499
|
var import_path105 = __toESM(require("path"), 1);
|
|
938751
939500
|
|
|
938752
939501
|
// src/commands/write-translation/translation-service.ts
|
|
@@ -939603,7 +940352,7 @@ async function writeTranslationForProject({
|
|
|
939603
940352
|
}
|
|
939604
940353
|
const totalFilesToProcess = filesToProcess.length + (docsConfigHasChanged ? 1 : 0);
|
|
939605
940354
|
const totalProgressItems = totalFilesToProcess * targetLanguages.length;
|
|
939606
|
-
const useProgressBar = process.stdout.isTTY && !
|
|
940355
|
+
const useProgressBar = process.stdout.isTTY && !import_is_ci4.default && totalProgressItems > 0;
|
|
939607
940356
|
let progressBar;
|
|
939608
940357
|
if (useProgressBar) {
|
|
939609
940358
|
progressBar = new import_cli_progress2.default.SingleBar({
|