@fern-api/replay 0.19.0 → 0.19.1
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/dist/cli.cjs +95 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +95 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +95 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2082,6 +2082,51 @@ async function buildMergePlan(args) {
|
|
|
2082
2082
|
// src/applicator/runMergeAndRecord.ts
|
|
2083
2083
|
var import_promises2 = require("fs/promises");
|
|
2084
2084
|
var import_node_path4 = require("path");
|
|
2085
|
+
|
|
2086
|
+
// src/autoversion.ts
|
|
2087
|
+
var AUTOVERSION_PLACEHOLDERS = [
|
|
2088
|
+
"v0.0.0-fern-placeholder",
|
|
2089
|
+
// Go -- a superstring of the default; check first
|
|
2090
|
+
"0.0.0-fern-placeholder",
|
|
2091
|
+
// TS/Java/C#/Ruby/PHP/Rust/default
|
|
2092
|
+
"0.0.0.dev0"
|
|
2093
|
+
// Python (PEP 440 -- Poetry rejects hyphens)
|
|
2094
|
+
];
|
|
2095
|
+
var VERSION_TOKEN = /(?:v)?\d+\.\d+\.\d+[0-9A-Za-z.+-]*/g;
|
|
2096
|
+
var VERSION_MASK = "@@FERN_VERSION@@";
|
|
2097
|
+
function maskVersions(line) {
|
|
2098
|
+
return line.replace(VERSION_TOKEN, VERSION_MASK);
|
|
2099
|
+
}
|
|
2100
|
+
function containsPlaceholder(content) {
|
|
2101
|
+
if (!content) return false;
|
|
2102
|
+
return AUTOVERSION_PLACEHOLDERS.some((p) => content.includes(p));
|
|
2103
|
+
}
|
|
2104
|
+
function remapManagedLines(genBase, target, theirs) {
|
|
2105
|
+
if (!containsPlaceholder(genBase)) return theirs;
|
|
2106
|
+
const managedMasks = /* @__PURE__ */ new Set();
|
|
2107
|
+
for (const line of genBase.split("\n")) {
|
|
2108
|
+
if (AUTOVERSION_PLACEHOLDERS.some((p) => line.includes(p))) managedMasks.add(maskVersions(line));
|
|
2109
|
+
}
|
|
2110
|
+
if (managedMasks.size === 0) return theirs;
|
|
2111
|
+
const targetByMask = /* @__PURE__ */ new Map();
|
|
2112
|
+
for (const line of target.split("\n")) {
|
|
2113
|
+
const mask = maskVersions(line);
|
|
2114
|
+
if (managedMasks.has(mask) && !targetByMask.has(mask)) targetByMask.set(mask, line);
|
|
2115
|
+
}
|
|
2116
|
+
return theirs.split("\n").map((line) => {
|
|
2117
|
+
const mask = maskVersions(line);
|
|
2118
|
+
const replacement = managedMasks.has(mask) ? targetByMask.get(mask) : void 0;
|
|
2119
|
+
return replacement !== void 0 && replacement !== line ? replacement : line;
|
|
2120
|
+
}).join("\n");
|
|
2121
|
+
}
|
|
2122
|
+
function neutralizeAutoversionTheirs(genBase, theirs) {
|
|
2123
|
+
return remapManagedLines(genBase, genBase, theirs);
|
|
2124
|
+
}
|
|
2125
|
+
function alignAutoversionToOurs(genBase, ours, theirs) {
|
|
2126
|
+
return remapManagedLines(genBase, ours, theirs);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
// src/applicator/runMergeAndRecord.ts
|
|
2085
2130
|
async function runMergeAndRecord(args) {
|
|
2086
2131
|
const { plan, inputs, patch, accumulator } = args;
|
|
2087
2132
|
const { resolvedPath, metadata, oursPath, ours } = inputs;
|
|
@@ -2112,11 +2157,13 @@ async function runMergeAndRecord(args) {
|
|
|
2112
2157
|
accumulator.recordMerge(resolvedPath, merged2.content, patch.base_generation);
|
|
2113
2158
|
return { file: resolvedPath, status: "merged", ...unreachableFlag };
|
|
2114
2159
|
}
|
|
2115
|
-
const
|
|
2160
|
+
const mergeBase = inputs.base != null ? alignAutoversionToOurs(inputs.base, ours, plan.mergeBase) : plan.mergeBase;
|
|
2161
|
+
const theirs = inputs.base != null ? alignAutoversionToOurs(inputs.base, ours, plan.theirs) : plan.theirs;
|
|
2162
|
+
const merged = threeWayMerge(mergeBase, ours, theirs);
|
|
2116
2163
|
await (0, import_promises2.mkdir)((0, import_node_path4.dirname)(oursPath), { recursive: true });
|
|
2117
2164
|
await (0, import_promises2.writeFile)(oursPath, merged.content);
|
|
2118
2165
|
if (merged.hasConflicts) {
|
|
2119
|
-
accumulator.recordConflict(resolvedPath,
|
|
2166
|
+
accumulator.recordConflict(resolvedPath, theirs, patch.base_generation);
|
|
2120
2167
|
return {
|
|
2121
2168
|
file: resolvedPath,
|
|
2122
2169
|
status: "conflict",
|
|
@@ -2125,7 +2172,7 @@ async function runMergeAndRecord(args) {
|
|
|
2125
2172
|
conflictMetadata: metadata
|
|
2126
2173
|
};
|
|
2127
2174
|
}
|
|
2128
|
-
accumulator.recordMerge(resolvedPath,
|
|
2175
|
+
accumulator.recordMerge(resolvedPath, theirs, patch.base_generation);
|
|
2129
2176
|
return {
|
|
2130
2177
|
file: resolvedPath,
|
|
2131
2178
|
status: "merged",
|
|
@@ -3911,7 +3958,9 @@ var ReplayService = class {
|
|
|
3911
3958
|
if (baseChanged) repointed++;
|
|
3912
3959
|
continue;
|
|
3913
3960
|
}
|
|
3914
|
-
const
|
|
3961
|
+
const rawDiff = await this.git.exec(["diff", currentGenSha, "--", ...patch.files]).catch(() => null);
|
|
3962
|
+
const neutralizedRebase = rawDiff != null && containsPlaceholder(rawDiff) ? await this.computeAutoversionNeutralizedRebase(patch.files, currentGenSha) : null;
|
|
3963
|
+
const diff = neutralizedRebase ? neutralizedRebase.diff : rawDiff;
|
|
3915
3964
|
if (diff === null) continue;
|
|
3916
3965
|
if (!diff.trim()) {
|
|
3917
3966
|
if (hasProtectedFiles) {
|
|
@@ -3967,7 +4016,7 @@ var ReplayService = class {
|
|
|
3967
4016
|
patch.base_generation = currentGenSha;
|
|
3968
4017
|
patch.patch_content = diff;
|
|
3969
4018
|
patch.content_hash = newContentHash;
|
|
3970
|
-
const snapshot = await this.readSnapshotFromDisk(patch.files);
|
|
4019
|
+
const snapshot = neutralizedRebase ? neutralizedRebase.snapshot : await this.readSnapshotFromDisk(patch.files);
|
|
3971
4020
|
const snapshotIsNonEmpty = Object.keys(snapshot).length > 0;
|
|
3972
4021
|
if (snapshotIsNonEmpty) {
|
|
3973
4022
|
patch.theirs_snapshot = snapshot;
|
|
@@ -4007,6 +4056,47 @@ var ReplayService = class {
|
|
|
4007
4056
|
}
|
|
4008
4057
|
return snapshot;
|
|
4009
4058
|
}
|
|
4059
|
+
/**
|
|
4060
|
+
* Autoversion-aware replacement for the naive `git diff currentGen -- files`
|
|
4061
|
+
* + on-disk snapshot used when rebasing a cleanly-applied patch.
|
|
4062
|
+
*
|
|
4063
|
+
* The generation tree (BASE) still carries the placeholder on version lines
|
|
4064
|
+
* (autoversion runs after `[fern-generated]`), but the working tree holds
|
|
4065
|
+
* autoversion's resolved semver. A raw diff/snapshot would fold the
|
|
4066
|
+
* placeholder->semver swap into the customer's patch_content/theirs_snapshot,
|
|
4067
|
+
* pinning a stale version. This re-derives both against a version-neutralized
|
|
4068
|
+
* THEIRS so the pipeline-owned version line never enters the patch.
|
|
4069
|
+
*
|
|
4070
|
+
* Returns null when no file is under autoversion (fixed `--version`, i.e. no
|
|
4071
|
+
* placeholder in the generation tree) — callers keep their existing behavior.
|
|
4072
|
+
*/
|
|
4073
|
+
async computeAutoversionNeutralizedRebase(files, currentGenSha) {
|
|
4074
|
+
const genByFile = /* @__PURE__ */ new Map();
|
|
4075
|
+
let anyPlaceholder = false;
|
|
4076
|
+
for (const file of files) {
|
|
4077
|
+
const gen = await this.git.showFile(currentGenSha, file).catch(() => null);
|
|
4078
|
+
genByFile.set(file, gen);
|
|
4079
|
+
if (containsPlaceholder(gen)) anyPlaceholder = true;
|
|
4080
|
+
}
|
|
4081
|
+
if (!anyPlaceholder) return null;
|
|
4082
|
+
const snapshot = {};
|
|
4083
|
+
const fragments = [];
|
|
4084
|
+
for (const file of files) {
|
|
4085
|
+
if (isBinaryFile(file)) continue;
|
|
4086
|
+
const disk = await (0, import_promises6.readFile)((0, import_node_path9.join)(this.outputDir, file), "utf-8").catch(() => null);
|
|
4087
|
+
if (disk == null) continue;
|
|
4088
|
+
const gen = genByFile.get(file) ?? null;
|
|
4089
|
+
const neutralized = gen != null ? neutralizeAutoversionTheirs(gen, disk) : disk;
|
|
4090
|
+
snapshot[file] = neutralized;
|
|
4091
|
+
if (gen == null) {
|
|
4092
|
+
fragments.push(synthesizeNewFileDiff(file, neutralized));
|
|
4093
|
+
} else if (gen !== neutralized) {
|
|
4094
|
+
const fileDiff = await unifiedDiff(gen, neutralized, file);
|
|
4095
|
+
if (fileDiff.trim()) fragments.push(fileDiff);
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
return { diff: fragments.join(""), snapshot };
|
|
4099
|
+
}
|
|
4010
4100
|
/**
|
|
4011
4101
|
* Detects autoversion contamination — returns true when a
|
|
4012
4102
|
* `[fern-autoversion]` commit between `fromSha` and `toSha` touched any
|