@fern-api/replay 0.14.0 → 0.14.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 +3182 -2855
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +1748 -1417
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -2
- package/dist/index.d.ts +86 -2
- package/dist/index.js +1747 -1417
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -37,6 +37,31 @@ interface StoredPatch {
|
|
|
37
37
|
* the generator will not start producing a file it never produced before.
|
|
38
38
|
*/
|
|
39
39
|
user_owned?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* THEIRS snapshot — the post-customer-edit content of each file the patch
|
|
42
|
+
* touches. When present, replay's slow-path 3-way merge uses this directly
|
|
43
|
+
* as THEIRS, bypassing `applyPatchToContent(BASE, patch_content)`
|
|
44
|
+
* reconstruction.
|
|
45
|
+
*
|
|
46
|
+
* Why this exists: `patch_content` is a unified diff anchored to specific
|
|
47
|
+
* line numbers in BASE. When the generator structurally changes a file
|
|
48
|
+
* across regens (different types, dropped/added imports, etc.), those
|
|
49
|
+
* line anchors no longer survive — reconstruction fails or produces
|
|
50
|
+
* garbage, and the customer's customization gets lost. The snapshot
|
|
51
|
+
* stores the customer's intent at content level, not at line-anchor
|
|
52
|
+
* level. diff3 then operates on actual content (BASE vs OURS vs THEIRS)
|
|
53
|
+
* and naturally handles structural changes via merge semantics rather
|
|
54
|
+
* than line bookkeeping.
|
|
55
|
+
*
|
|
56
|
+
* Keys are post-rename paths (matching the `files` array). Files the
|
|
57
|
+
* patch deletes are omitted from the map; the `+++ /dev/null` marker
|
|
58
|
+
* in `patch_content` remains the canonical delete signal.
|
|
59
|
+
*
|
|
60
|
+
* Optional for backward compat. Existing lockfiles without this field
|
|
61
|
+
* auto-migrate on the first regen post-upgrade via
|
|
62
|
+
* `preGenerationRebase`'s migration pass.
|
|
63
|
+
*/
|
|
64
|
+
theirs_snapshot?: Record<string, string>;
|
|
40
65
|
}
|
|
41
66
|
interface CustomizationsConfig {
|
|
42
67
|
exclude?: string[];
|
|
@@ -199,14 +224,14 @@ declare class LockfileManager {
|
|
|
199
224
|
save(): void;
|
|
200
225
|
addGeneration(record: GenerationRecord): void;
|
|
201
226
|
addPatch(patch: StoredPatch): void;
|
|
202
|
-
updatePatch(patchId: string, updates: Partial<Pick<StoredPatch, "base_generation" | "patch_content" | "content_hash" | "files" | "status" | "user_owned">>): void;
|
|
227
|
+
updatePatch(patchId: string, updates: Partial<Pick<StoredPatch, "base_generation" | "patch_content" | "content_hash" | "files" | "status" | "user_owned" | "theirs_snapshot">>): void;
|
|
203
228
|
removePatch(patchId: string): void;
|
|
204
229
|
clearPatches(): void;
|
|
205
230
|
addForgottenHash(hash: string): void;
|
|
206
231
|
getUnresolvedPatches(): StoredPatch[];
|
|
207
232
|
getResolvingPatches(): StoredPatch[];
|
|
208
233
|
markPatchUnresolved(patchId: string): void;
|
|
209
|
-
markPatchResolved(patchId: string, updates: Pick<StoredPatch, "patch_content" | "content_hash" | "base_generation" | "files">): void;
|
|
234
|
+
markPatchResolved(patchId: string, updates: Pick<StoredPatch, "patch_content" | "content_hash" | "base_generation" | "files" | "theirs_snapshot">): void;
|
|
210
235
|
getPatches(): StoredPatch[];
|
|
211
236
|
setReplaySkippedAt(timestamp: string): void;
|
|
212
237
|
clearReplaySkippedAt(): void;
|
|
@@ -376,6 +401,26 @@ declare class ReplayApplicator {
|
|
|
376
401
|
* cross-patch context mismatch
|
|
377
402
|
*/
|
|
378
403
|
private currentApplyMode;
|
|
404
|
+
/**
|
|
405
|
+
* Set of files that appear in 2+ patches in the current `applyPatches`
|
|
406
|
+
* invocation. Computed at the top of `applyPatches`. Read by `mergeFile`
|
|
407
|
+
* and `populateAccumulatorForPatch` to decide whether to use the patch's
|
|
408
|
+
* `theirs_snapshot` directly as THEIRS (snapshot-as-primary) or fall
|
|
409
|
+
* back to line-anchored reconstruction (FER-9525 cross-patch isolation).
|
|
410
|
+
*
|
|
411
|
+
* Snapshot-as-primary is correct when the patch owns its file (no other
|
|
412
|
+
* patch in this run touches it): the snapshot is what the customer
|
|
413
|
+
* intends for that file post-regen, regardless of generator structural
|
|
414
|
+
* changes that would invalidate the diff's line anchors.
|
|
415
|
+
*
|
|
416
|
+
* When a file IS shared, snapshots are CUMULATIVE across the patches
|
|
417
|
+
* touching it (each one's snapshot includes prior patches' contributions).
|
|
418
|
+
* Using a later patch's cumulative snapshot as its individual THEIRS
|
|
419
|
+
* would propagate prior patches' edits into the merge — surfacing nested
|
|
420
|
+
* conflicts at regions the patch alone never touched. Reconstruction
|
|
421
|
+
* via `applyPatchToContent` is required there.
|
|
422
|
+
*/
|
|
423
|
+
private sharedFiles;
|
|
379
424
|
constructor(git: GitClient, lockManager: LockfileManager, outputDir: string);
|
|
380
425
|
/**
|
|
381
426
|
* Resolve the GenerationRecord for a patch's base_generation.
|
|
@@ -680,6 +725,45 @@ declare class ReplayService {
|
|
|
680
725
|
* Returns the number of patches rebased.
|
|
681
726
|
*/
|
|
682
727
|
private rebasePatches;
|
|
728
|
+
/**
|
|
729
|
+
* Read THEIRS snapshot (post-customer-edit content) for a list of files
|
|
730
|
+
* from the working tree on disk. Used by `rebasePatches` after a clean
|
|
731
|
+
* apply — disk = correctly-merged customer state at the new generation.
|
|
732
|
+
* Skips binary files (matches `mergeFile`/`populateAccumulatorForPatch`
|
|
733
|
+
* policy — binary content as `utf-8` is lossy and snapshots of it would
|
|
734
|
+
* never be used during apply anyway).
|
|
735
|
+
*/
|
|
736
|
+
private readSnapshotFromDisk;
|
|
737
|
+
/**
|
|
738
|
+
* Read THEIRS snapshot from a git tree-ish (commit, branch, or tree).
|
|
739
|
+
* Used when the diff that produced `patch_content` was relative to that
|
|
740
|
+
* tree (e.g., `git diff currentGen HEAD --` → snapshot from HEAD).
|
|
741
|
+
* Skips binary files.
|
|
742
|
+
*/
|
|
743
|
+
private readSnapshotFromTree;
|
|
744
|
+
/**
|
|
745
|
+
* One-shot auto-migration: backfill `theirs_snapshot` on existing
|
|
746
|
+
* patches that predate the snapshot encoding. Computes the snapshot by
|
|
747
|
+
* applying the patch's `patch_content` to its `base_generation` tree —
|
|
748
|
+
* yielding the patch's INDIVIDUAL contribution.
|
|
749
|
+
*
|
|
750
|
+
* **Skips patches sharing files with other patches.** HEAD's content
|
|
751
|
+
* for a file shared by N patches is cumulative across all of them, so
|
|
752
|
+
* a HEAD-fallback would falsely encode other patches' contributions
|
|
753
|
+
* into one patch's snapshot — breaking FER-9525 cross-patch isolation
|
|
754
|
+
* if the snapshot fallback later fires. Per-patch snapshots only
|
|
755
|
+
* make sense when the patch owns its file. Multi-patch-same-file
|
|
756
|
+
* legacy lockfiles keep using line-anchored reconstruction (the
|
|
757
|
+
* existing path); they're no worse than pre-upgrade.
|
|
758
|
+
*
|
|
759
|
+
* **Does not fall back to HEAD when BASE-reconstruction fails.** Same
|
|
760
|
+
* reason — HEAD content can be cumulative. Patches whose
|
|
761
|
+
* reconstruction fails just don't get a snapshot; legacy paths
|
|
762
|
+
* (PR #73's by-association gate, accumulator fallback) handle them.
|
|
763
|
+
*
|
|
764
|
+
* Skips patches that already have a snapshot.
|
|
765
|
+
*/
|
|
766
|
+
private migratePatchesToSnapshot;
|
|
683
767
|
/**
|
|
684
768
|
* Determine whether `file` is user-owned (never produced by the generator).
|
|
685
769
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,31 @@ interface StoredPatch {
|
|
|
37
37
|
* the generator will not start producing a file it never produced before.
|
|
38
38
|
*/
|
|
39
39
|
user_owned?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* THEIRS snapshot — the post-customer-edit content of each file the patch
|
|
42
|
+
* touches. When present, replay's slow-path 3-way merge uses this directly
|
|
43
|
+
* as THEIRS, bypassing `applyPatchToContent(BASE, patch_content)`
|
|
44
|
+
* reconstruction.
|
|
45
|
+
*
|
|
46
|
+
* Why this exists: `patch_content` is a unified diff anchored to specific
|
|
47
|
+
* line numbers in BASE. When the generator structurally changes a file
|
|
48
|
+
* across regens (different types, dropped/added imports, etc.), those
|
|
49
|
+
* line anchors no longer survive — reconstruction fails or produces
|
|
50
|
+
* garbage, and the customer's customization gets lost. The snapshot
|
|
51
|
+
* stores the customer's intent at content level, not at line-anchor
|
|
52
|
+
* level. diff3 then operates on actual content (BASE vs OURS vs THEIRS)
|
|
53
|
+
* and naturally handles structural changes via merge semantics rather
|
|
54
|
+
* than line bookkeeping.
|
|
55
|
+
*
|
|
56
|
+
* Keys are post-rename paths (matching the `files` array). Files the
|
|
57
|
+
* patch deletes are omitted from the map; the `+++ /dev/null` marker
|
|
58
|
+
* in `patch_content` remains the canonical delete signal.
|
|
59
|
+
*
|
|
60
|
+
* Optional for backward compat. Existing lockfiles without this field
|
|
61
|
+
* auto-migrate on the first regen post-upgrade via
|
|
62
|
+
* `preGenerationRebase`'s migration pass.
|
|
63
|
+
*/
|
|
64
|
+
theirs_snapshot?: Record<string, string>;
|
|
40
65
|
}
|
|
41
66
|
interface CustomizationsConfig {
|
|
42
67
|
exclude?: string[];
|
|
@@ -199,14 +224,14 @@ declare class LockfileManager {
|
|
|
199
224
|
save(): void;
|
|
200
225
|
addGeneration(record: GenerationRecord): void;
|
|
201
226
|
addPatch(patch: StoredPatch): void;
|
|
202
|
-
updatePatch(patchId: string, updates: Partial<Pick<StoredPatch, "base_generation" | "patch_content" | "content_hash" | "files" | "status" | "user_owned">>): void;
|
|
227
|
+
updatePatch(patchId: string, updates: Partial<Pick<StoredPatch, "base_generation" | "patch_content" | "content_hash" | "files" | "status" | "user_owned" | "theirs_snapshot">>): void;
|
|
203
228
|
removePatch(patchId: string): void;
|
|
204
229
|
clearPatches(): void;
|
|
205
230
|
addForgottenHash(hash: string): void;
|
|
206
231
|
getUnresolvedPatches(): StoredPatch[];
|
|
207
232
|
getResolvingPatches(): StoredPatch[];
|
|
208
233
|
markPatchUnresolved(patchId: string): void;
|
|
209
|
-
markPatchResolved(patchId: string, updates: Pick<StoredPatch, "patch_content" | "content_hash" | "base_generation" | "files">): void;
|
|
234
|
+
markPatchResolved(patchId: string, updates: Pick<StoredPatch, "patch_content" | "content_hash" | "base_generation" | "files" | "theirs_snapshot">): void;
|
|
210
235
|
getPatches(): StoredPatch[];
|
|
211
236
|
setReplaySkippedAt(timestamp: string): void;
|
|
212
237
|
clearReplaySkippedAt(): void;
|
|
@@ -376,6 +401,26 @@ declare class ReplayApplicator {
|
|
|
376
401
|
* cross-patch context mismatch
|
|
377
402
|
*/
|
|
378
403
|
private currentApplyMode;
|
|
404
|
+
/**
|
|
405
|
+
* Set of files that appear in 2+ patches in the current `applyPatches`
|
|
406
|
+
* invocation. Computed at the top of `applyPatches`. Read by `mergeFile`
|
|
407
|
+
* and `populateAccumulatorForPatch` to decide whether to use the patch's
|
|
408
|
+
* `theirs_snapshot` directly as THEIRS (snapshot-as-primary) or fall
|
|
409
|
+
* back to line-anchored reconstruction (FER-9525 cross-patch isolation).
|
|
410
|
+
*
|
|
411
|
+
* Snapshot-as-primary is correct when the patch owns its file (no other
|
|
412
|
+
* patch in this run touches it): the snapshot is what the customer
|
|
413
|
+
* intends for that file post-regen, regardless of generator structural
|
|
414
|
+
* changes that would invalidate the diff's line anchors.
|
|
415
|
+
*
|
|
416
|
+
* When a file IS shared, snapshots are CUMULATIVE across the patches
|
|
417
|
+
* touching it (each one's snapshot includes prior patches' contributions).
|
|
418
|
+
* Using a later patch's cumulative snapshot as its individual THEIRS
|
|
419
|
+
* would propagate prior patches' edits into the merge — surfacing nested
|
|
420
|
+
* conflicts at regions the patch alone never touched. Reconstruction
|
|
421
|
+
* via `applyPatchToContent` is required there.
|
|
422
|
+
*/
|
|
423
|
+
private sharedFiles;
|
|
379
424
|
constructor(git: GitClient, lockManager: LockfileManager, outputDir: string);
|
|
380
425
|
/**
|
|
381
426
|
* Resolve the GenerationRecord for a patch's base_generation.
|
|
@@ -680,6 +725,45 @@ declare class ReplayService {
|
|
|
680
725
|
* Returns the number of patches rebased.
|
|
681
726
|
*/
|
|
682
727
|
private rebasePatches;
|
|
728
|
+
/**
|
|
729
|
+
* Read THEIRS snapshot (post-customer-edit content) for a list of files
|
|
730
|
+
* from the working tree on disk. Used by `rebasePatches` after a clean
|
|
731
|
+
* apply — disk = correctly-merged customer state at the new generation.
|
|
732
|
+
* Skips binary files (matches `mergeFile`/`populateAccumulatorForPatch`
|
|
733
|
+
* policy — binary content as `utf-8` is lossy and snapshots of it would
|
|
734
|
+
* never be used during apply anyway).
|
|
735
|
+
*/
|
|
736
|
+
private readSnapshotFromDisk;
|
|
737
|
+
/**
|
|
738
|
+
* Read THEIRS snapshot from a git tree-ish (commit, branch, or tree).
|
|
739
|
+
* Used when the diff that produced `patch_content` was relative to that
|
|
740
|
+
* tree (e.g., `git diff currentGen HEAD --` → snapshot from HEAD).
|
|
741
|
+
* Skips binary files.
|
|
742
|
+
*/
|
|
743
|
+
private readSnapshotFromTree;
|
|
744
|
+
/**
|
|
745
|
+
* One-shot auto-migration: backfill `theirs_snapshot` on existing
|
|
746
|
+
* patches that predate the snapshot encoding. Computes the snapshot by
|
|
747
|
+
* applying the patch's `patch_content` to its `base_generation` tree —
|
|
748
|
+
* yielding the patch's INDIVIDUAL contribution.
|
|
749
|
+
*
|
|
750
|
+
* **Skips patches sharing files with other patches.** HEAD's content
|
|
751
|
+
* for a file shared by N patches is cumulative across all of them, so
|
|
752
|
+
* a HEAD-fallback would falsely encode other patches' contributions
|
|
753
|
+
* into one patch's snapshot — breaking FER-9525 cross-patch isolation
|
|
754
|
+
* if the snapshot fallback later fires. Per-patch snapshots only
|
|
755
|
+
* make sense when the patch owns its file. Multi-patch-same-file
|
|
756
|
+
* legacy lockfiles keep using line-anchored reconstruction (the
|
|
757
|
+
* existing path); they're no worse than pre-upgrade.
|
|
758
|
+
*
|
|
759
|
+
* **Does not fall back to HEAD when BASE-reconstruction fails.** Same
|
|
760
|
+
* reason — HEAD content can be cumulative. Patches whose
|
|
761
|
+
* reconstruction fails just don't get a snapshot; legacy paths
|
|
762
|
+
* (PR #73's by-association gate, accumulator fallback) handle them.
|
|
763
|
+
*
|
|
764
|
+
* Skips patches that already have a snapshot.
|
|
765
|
+
*/
|
|
766
|
+
private migratePatchesToSnapshot;
|
|
683
767
|
/**
|
|
684
768
|
* Determine whether `file` is user-owned (never produced by the generator).
|
|
685
769
|
*
|