@fern-api/replay 0.15.0 → 0.15.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/README.md +19 -7
- package/dist/cli.cjs +3500 -3312
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +2016 -1829
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +197 -135
- package/dist/index.d.ts +197 -135
- package/dist/index.js +1982 -1795
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -133,6 +133,16 @@ interface CommitInfo {
|
|
|
133
133
|
interface ReplayConfig {
|
|
134
134
|
enabled: boolean;
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* The four mutually-exclusive code paths a `runReplay()` invocation can take.
|
|
138
|
+
* Selected by `ReplayService.selectFlow()` based on lockfile state and options.
|
|
139
|
+
*
|
|
140
|
+
* - `first-generation` — no lockfile yet; this run creates the initial generation record.
|
|
141
|
+
* - `no-patches` — lockfile exists with zero patches; only newly detected patches apply.
|
|
142
|
+
* - `normal-regeneration` — lockfile exists with patches; full pre-rebase + apply + post-rebase pipeline.
|
|
143
|
+
* - `skip-application` — `--no-replay` mode; commit generation, set replay-skipped marker, return early.
|
|
144
|
+
*/
|
|
145
|
+
type FlowKind = "first-generation" | "no-patches" | "normal-regeneration" | "skip-application";
|
|
136
146
|
|
|
137
147
|
/**
|
|
138
148
|
* Credential detection patterns for the replay pipeline.
|
|
@@ -143,8 +153,6 @@ interface ReplayConfig {
|
|
|
143
153
|
*
|
|
144
154
|
* These patterns mirror the invariant assertions in __tests__/invariants/i4-credential-containment.ts.
|
|
145
155
|
* If they diverge, the invariant tests independently catch real leaks.
|
|
146
|
-
*
|
|
147
|
-
* @security FER-9807
|
|
148
156
|
*/
|
|
149
157
|
interface CredentialPattern {
|
|
150
158
|
name: string;
|
|
@@ -253,7 +261,7 @@ declare class ReplayDetector {
|
|
|
253
261
|
constructor(git: GitClient, lockManager: LockfileManager, sdkOutputDir: string);
|
|
254
262
|
detectNewPatches(): Promise<DetectionResult>;
|
|
255
263
|
/**
|
|
256
|
-
*
|
|
264
|
+
* Non-linear-history detection via `merge-base(prevGen, HEAD)`.
|
|
257
265
|
*
|
|
258
266
|
* After a squash-merge of a Fern-bot PR, `lastGen.commit_sha` (the previous
|
|
259
267
|
* `[fern-generated]`) is orphaned but still in git's object database. The
|
|
@@ -268,7 +276,7 @@ declare class ReplayDetector {
|
|
|
268
276
|
*/
|
|
269
277
|
private detectPatchesViaMergeBase;
|
|
270
278
|
/**
|
|
271
|
-
*
|
|
279
|
+
* Walk `${rangeStart}..HEAD`, classify each commit, emit one
|
|
272
280
|
* `StoredPatch` per customer commit. Body shared by the linear path
|
|
273
281
|
* (rangeStart = lastGen.commit_sha) and the merge-base path.
|
|
274
282
|
*
|
|
@@ -292,12 +300,12 @@ declare class ReplayDetector {
|
|
|
292
300
|
*
|
|
293
301
|
* This ensures content hashes match across the no-patches and normal-
|
|
294
302
|
* regeneration flows, which store formatPatch and git-diff formats
|
|
295
|
-
* respectively
|
|
303
|
+
* respectively.
|
|
296
304
|
*/
|
|
297
305
|
computeContentHash(patchContent: string): string;
|
|
298
306
|
/**
|
|
299
|
-
*
|
|
300
|
-
*
|
|
307
|
+
* Drop patches whose files were transiently created and then deleted
|
|
308
|
+
* within the current linear detection window, and are absent from
|
|
301
309
|
* HEAD at the end of the window.
|
|
302
310
|
*
|
|
303
311
|
* Rationale: on a merge commit, createMergeCompositePatch already diffs
|
|
@@ -322,8 +330,8 @@ declare class ReplayDetector {
|
|
|
322
330
|
* single files from a multi-file patch would require a follow-up that
|
|
323
331
|
* regenerates the patch content via `git format-patch -- <files>`. No
|
|
324
332
|
* current failing test exercises this, and leaving the patch intact
|
|
325
|
-
* preserves today's behaviour.
|
|
326
|
-
*
|
|
333
|
+
* preserves today's behaviour. Revisit if a future adversarial test
|
|
334
|
+
* demands per-file stripping.
|
|
327
335
|
*/
|
|
328
336
|
private collapseNetZeroFiles;
|
|
329
337
|
/**
|
|
@@ -381,46 +389,27 @@ declare class ReplayDetector {
|
|
|
381
389
|
*/
|
|
382
390
|
declare function threeWayMerge(base: string, ours: string, theirs: string): MergeResult;
|
|
383
391
|
|
|
392
|
+
interface AccumulatorEntry {
|
|
393
|
+
readonly content: string;
|
|
394
|
+
readonly baseGeneration: string;
|
|
395
|
+
}
|
|
396
|
+
|
|
384
397
|
declare class ReplayApplicator {
|
|
385
398
|
private git;
|
|
386
399
|
private lockManager;
|
|
387
400
|
private outputDir;
|
|
388
401
|
private renameCache;
|
|
389
402
|
private treeExistsCache;
|
|
390
|
-
private fileTheirsAccumulator;
|
|
391
|
-
/**
|
|
392
|
-
* Apply mode for the current `applyPatches` invocation. Set at the start
|
|
393
|
-
* of `applyPatches`, read by `mergeFile` to decide:
|
|
394
|
-
* - whether to skip the intra-loop marker strip (kept in `applyPatches`)
|
|
395
|
-
* - whether to populate the accumulator after a conflicted merge
|
|
396
|
-
* (resolve mode populates so subsequent patches on the same file
|
|
397
|
-
* can use patch A's THEIRS as a structurally-correct merge base
|
|
398
|
-
* when their diff was authored against post-A structure)
|
|
399
|
-
* - whether to retry THEIRS reconstruction against the accumulator
|
|
400
|
-
* when the BASE-relative reconstruction produced markers from a
|
|
401
|
-
* cross-patch context mismatch
|
|
402
|
-
*/
|
|
403
|
-
private currentApplyMode;
|
|
404
403
|
/**
|
|
405
|
-
*
|
|
406
|
-
* invocation
|
|
407
|
-
*
|
|
408
|
-
* `theirs_snapshot` directly as THEIRS (snapshot-as-primary) or fall
|
|
409
|
-
* back to line-anchored reconstruction (FER-9525 cross-patch isolation).
|
|
404
|
+
* Inter-patch THEIRS coordination. Created fresh in each `applyPatches`
|
|
405
|
+
* invocation with the run's apply mode + precomputed shared-file set.
|
|
406
|
+
* See `src/accumulator/MergeAccumulator.ts` for the full contract.
|
|
410
407
|
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
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.
|
|
408
|
+
* Initialized to an empty default in the constructor so type narrowing
|
|
409
|
+
* works for callers that read it before any `applyPatches` invocation
|
|
410
|
+
* (e.g., the `getAccumulatorSnapshot` test accessor).
|
|
422
411
|
*/
|
|
423
|
-
private
|
|
412
|
+
private accumulator;
|
|
424
413
|
constructor(git: GitClient, lockManager: LockfileManager, outputDir: string);
|
|
425
414
|
/**
|
|
426
415
|
* Resolve the GenerationRecord for a patch's base_generation.
|
|
@@ -428,19 +417,14 @@ declare class ReplayApplicator {
|
|
|
428
417
|
* when base_generation isn't a tracked generation (commit-scan patches).
|
|
429
418
|
*/
|
|
430
419
|
private resolveBaseGeneration;
|
|
431
|
-
/** Reset inter-patch accumulator for a new cycle. */
|
|
432
|
-
private resetAccumulator;
|
|
433
420
|
/**
|
|
434
421
|
* @internal Test-only.
|
|
435
|
-
* Returns a defensive copy of the inter-patch accumulator. Used by
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
422
|
+
* Returns a defensive copy of the inter-patch accumulator. Used by
|
|
423
|
+
* invariant I2 to assert that after every applied patch, the
|
|
424
|
+
* accumulator has an entry for each file the patch touched, and the
|
|
425
|
+
* entry's content matches on-disk.
|
|
439
426
|
*/
|
|
440
|
-
getAccumulatorSnapshot(): ReadonlyMap<string,
|
|
441
|
-
content: string;
|
|
442
|
-
baseGeneration: string;
|
|
443
|
-
}>;
|
|
427
|
+
getAccumulatorSnapshot(): ReadonlyMap<string, AccumulatorEntry>;
|
|
444
428
|
/**
|
|
445
429
|
* Apply all patches, returning results for each.
|
|
446
430
|
* Skips patches that match exclude patterns in replay.yml
|
|
@@ -513,6 +497,59 @@ declare class ReplayCommitter {
|
|
|
513
497
|
getTreeHash(commitSha: string): Promise<string>;
|
|
514
498
|
}
|
|
515
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Inputs for one ownership resolution call.
|
|
502
|
+
*
|
|
503
|
+
* `originalFileName` is the pre-rename path used when the generator moved
|
|
504
|
+
* the file between generations — tree lookups at ancestor generations need
|
|
505
|
+
* the original path because that's where the generator produced the content.
|
|
506
|
+
*
|
|
507
|
+
* `warnedGens` and `warnings` are mutable accumulators threaded across calls
|
|
508
|
+
* within a single replay run. The canonical path appends a one-time warning
|
|
509
|
+
* when a base generation tree is unreachable (shallow clone / aggressive GC)
|
|
510
|
+
* so the customer sees a single message per affected base, not one per file.
|
|
511
|
+
*/
|
|
512
|
+
interface OwnershipContext {
|
|
513
|
+
readonly file: string;
|
|
514
|
+
readonly originalFileName?: string;
|
|
515
|
+
readonly patch: StoredPatch;
|
|
516
|
+
readonly currentGenSha: string;
|
|
517
|
+
readonly fernignorePatterns: readonly string[];
|
|
518
|
+
readonly warnedGens: Set<string>;
|
|
519
|
+
readonly warnings: string[];
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Single canonical home for "is this file user-owned?" resolution. Every
|
|
523
|
+
* service-level check (preGenerationRebase, rebasePatches, trimAbsorbedFiles)
|
|
524
|
+
* routes through `resolveCanonical`.
|
|
525
|
+
*
|
|
526
|
+
* The earlier `rebasePatches` inline variant (`resolveAtCurrentGen`) was
|
|
527
|
+
* collapsed under FER-10386 once pre-flight investigation showed its
|
|
528
|
+
* narrower fall-back was harmless on every reachable scenario but worse
|
|
529
|
+
* on the unreachable-base-tree case. See `docs/known-divergences.md`
|
|
530
|
+
* entry 5 for the closed-out divergence record.
|
|
531
|
+
*/
|
|
532
|
+
declare class FileOwnership {
|
|
533
|
+
private readonly git;
|
|
534
|
+
constructor(git: GitClient);
|
|
535
|
+
/**
|
|
536
|
+
* Canonical resolution. Returns true if `file` is user-owned per:
|
|
537
|
+
*
|
|
538
|
+
* 1. `patch.user_owned === true` — authoritative, persisted flag.
|
|
539
|
+
* 2. `file === ".fernignore"` — always user-owned.
|
|
540
|
+
* 3. File matches a `.fernignore` pattern (via minimatch).
|
|
541
|
+
* 4. Patch content shows `--- /dev/null` for this file (legacy-safe
|
|
542
|
+
* signal for patches predating `user_owned` or whose flag was lost).
|
|
543
|
+
* 5. Tree-based check against `patch.base_generation` when reachable
|
|
544
|
+
* AND distinct from `currentGenSha`. Unreachable base → conservative
|
|
545
|
+
* user-owned (silent-absorption safety net), with a one-time warning
|
|
546
|
+
* per base SHA.
|
|
547
|
+
* 6. Final fallback: check `currentGenSha` when no usable base exists
|
|
548
|
+
* (legacy one-gen lockfile).
|
|
549
|
+
*/
|
|
550
|
+
resolveCanonical(ctx: OwnershipContext): Promise<boolean>;
|
|
551
|
+
}
|
|
552
|
+
|
|
516
553
|
interface ConflictDetail {
|
|
517
554
|
patchId: string;
|
|
518
555
|
patchMessage: string;
|
|
@@ -578,7 +615,7 @@ interface ApplyOptions {
|
|
|
578
615
|
* Contract for consumers (e.g., generator-cli pipeline inserting AutoVersionStep):
|
|
579
616
|
*
|
|
580
617
|
* - Between `prepareReplay` and `applyPreparedReplay`, HEAD is at the new
|
|
581
|
-
* `[fern-generated]` commit (unless `_prepared.
|
|
618
|
+
* `[fern-generated]` commit (unless `_prepared.kind === "terminal"`, e.g. dry-run).
|
|
582
619
|
* - Consumers MAY land additional commits on HEAD between phases. `applyPatches`
|
|
583
620
|
* runs against current HEAD at phase-2 call time, so mid-phase commits are
|
|
584
621
|
* respected. `rebasePatches` uses `currentGenerationSha` (the pure `[fern-generated]`
|
|
@@ -595,7 +632,7 @@ interface ApplyOptions {
|
|
|
595
632
|
* prep.previousGenerationSha != null && prep.flow !== "skip-application"
|
|
596
633
|
*/
|
|
597
634
|
interface ReplayPreparation {
|
|
598
|
-
flow:
|
|
635
|
+
flow: FlowKind;
|
|
599
636
|
/** lockfile.current_generation from BEFORE this run; null iff no prior generation exists */
|
|
600
637
|
previousGenerationSha: string | null;
|
|
601
638
|
/** SHA of the new [fern-generated] commit created by prepareReplay (or HEAD for dry-run terminals) */
|
|
@@ -605,12 +642,26 @@ interface ReplayPreparation {
|
|
|
605
642
|
/** @internal Opaque state consumed by applyPreparedReplay */
|
|
606
643
|
readonly _prepared: InternalPreparationState;
|
|
607
644
|
}
|
|
608
|
-
/**
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
645
|
+
/**
|
|
646
|
+
* @internal
|
|
647
|
+
* Opaque state passed from `prepareReplay` to `applyPreparedReplay`.
|
|
648
|
+
*
|
|
649
|
+
* Discriminated by `kind`:
|
|
650
|
+
* - `"terminal"` — phase 2 returns `preparedReport` without disk/git work
|
|
651
|
+
* (dry-run, first-generation with no patch work, no-patches with no
|
|
652
|
+
* detection results). Apply is a no-op.
|
|
653
|
+
* - `"active"` — phase 2 must apply patches, rebase, save lockfile, commit.
|
|
654
|
+
* Carries the patch set + cached phase-1 state.
|
|
655
|
+
*/
|
|
656
|
+
type InternalPreparationState = InternalPreparationTerminal | InternalPreparationActive;
|
|
657
|
+
interface InternalPreparationTerminal {
|
|
658
|
+
readonly kind: "terminal";
|
|
659
|
+
readonly flow: FlowKind;
|
|
660
|
+
readonly preparedReport: ReplayReport;
|
|
661
|
+
}
|
|
662
|
+
interface InternalPreparationActive {
|
|
663
|
+
readonly kind: "active";
|
|
664
|
+
readonly flow: FlowKind;
|
|
614
665
|
/** post-preGenRebase, post-detection, post-absorption (references, not copies) */
|
|
615
666
|
patchesToApply: StoredPatch[];
|
|
616
667
|
/** subset of patchesToApply that were freshly detected this cycle (vs already in the lockfile) */
|
|
@@ -629,33 +680,43 @@ interface InternalPreparationState {
|
|
|
629
680
|
optionsSnapshot?: ReplayOptions;
|
|
630
681
|
}
|
|
631
682
|
declare class ReplayService {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
683
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
684
|
+
git: GitClient;
|
|
685
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
686
|
+
detector: ReplayDetector;
|
|
687
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
688
|
+
applicator: ReplayApplicator;
|
|
689
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
690
|
+
committer: ReplayCommitter;
|
|
691
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
692
|
+
lockManager: LockfileManager;
|
|
693
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
694
|
+
fileOwnership: FileOwnership;
|
|
695
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
696
|
+
outputDir: string;
|
|
638
697
|
/** @internal Test-only. Captures the last ReplayResult[] returned from applicator.applyPatches(). */
|
|
639
|
-
|
|
698
|
+
_lastApplyResults: ReplayResult[];
|
|
640
699
|
/**
|
|
641
700
|
* Service-level warnings accumulated during a single runReplay() call.
|
|
642
701
|
* Merged with `detector.warnings` into `ReplayReport.warnings` by each flow handler.
|
|
643
|
-
* Populated by `
|
|
644
|
-
* (shallow clone / aggressive `git gc --prune`) and we fall back to a
|
|
645
|
-
* heuristic. Cleared at the top of `runReplay()`.
|
|
702
|
+
* Populated by `FileOwnership.resolveCanonical` when a patch's base generation tree
|
|
703
|
+
* is unreachable (shallow clone / aggressive `git gc --prune`) and we fall back to a
|
|
704
|
+
* conservative heuristic. Cleared at the top of `runReplay()`.
|
|
705
|
+
*
|
|
706
|
+
* @internal Used by Flow implementations in `src/flows/`.
|
|
646
707
|
*/
|
|
647
|
-
|
|
708
|
+
warnings: string[];
|
|
648
709
|
/**
|
|
649
710
|
* @internal Test-only accessor.
|
|
650
711
|
* Returns the ReplayApplicator instance used for the last run. Tests use this
|
|
651
|
-
* to inspect the inter-patch accumulator after runReplay() completes (
|
|
712
|
+
* to inspect the inter-patch accumulator after runReplay() completes (invariant I2).
|
|
652
713
|
*/
|
|
653
714
|
get applicatorRef(): ReplayApplicator;
|
|
654
715
|
/**
|
|
655
716
|
* @internal Test-only accessor.
|
|
656
717
|
* Returns the ReplayResult[] from the most recent applyPatches() call. Tests use
|
|
657
|
-
* this to filter applied-vs-conflicted-vs-absorbed patches for invariant
|
|
658
|
-
*
|
|
718
|
+
* this to filter applied-vs-conflicted-vs-absorbed patches for invariant
|
|
719
|
+
* assertions. Returns an empty array if no patches have been applied yet.
|
|
659
720
|
*/
|
|
660
721
|
getLastResults(): ReadonlyArray<ReplayResult>;
|
|
661
722
|
constructor(outputDir: string, _config: ReplayConfig);
|
|
@@ -682,6 +743,16 @@ declare class ReplayService {
|
|
|
682
743
|
* its own post-commit logic), this returns the precomputed report.
|
|
683
744
|
*/
|
|
684
745
|
applyPreparedReplay(prep: ReplayPreparation, options?: ApplyOptions): Promise<ReplayReport>;
|
|
746
|
+
/**
|
|
747
|
+
* Construct the flow for the current run. `selectFlow` is called from
|
|
748
|
+
* `prepareReplay` and decides based on options + lockfile state.
|
|
749
|
+
*/
|
|
750
|
+
private selectFlow;
|
|
751
|
+
/**
|
|
752
|
+
* Construct a flow given its kind. Used by `applyPreparedReplay` to
|
|
753
|
+
* route to the same kind that was selected in phase 1.
|
|
754
|
+
*/
|
|
755
|
+
private flowForKind;
|
|
685
756
|
/**
|
|
686
757
|
* Backwards-compatible atomic entry point. Composes `prepareReplay` + `applyPreparedReplay`.
|
|
687
758
|
* Behavior is byte-identical to the pre-split implementation for all existing callers.
|
|
@@ -703,28 +774,20 @@ declare class ReplayService {
|
|
|
703
774
|
baseBranchHead?: string;
|
|
704
775
|
}): Promise<void>;
|
|
705
776
|
private determineFlow;
|
|
706
|
-
private firstGenerationReport;
|
|
707
|
-
private _prepareFirstGeneration;
|
|
708
|
-
private _applyFirstGeneration;
|
|
709
|
-
/**
|
|
710
|
-
* Skip-application mode phase 1: commit the generation and update the
|
|
711
|
-
* in-memory lockfile, but don't detect or apply patches. Sets a marker
|
|
712
|
-
* so the next normal run skips revert detection in preGenerationRebase().
|
|
713
|
-
*
|
|
714
|
-
* Disk save is deferred to `_applySkipApplication`.
|
|
715
|
-
*/
|
|
716
|
-
private _prepareSkipApplication;
|
|
717
|
-
private _applySkipApplication;
|
|
718
|
-
private _prepareNoPatchesRegeneration;
|
|
719
|
-
private _applyNoPatchesRegeneration;
|
|
720
|
-
private _prepareNormalRegeneration;
|
|
721
|
-
private _applyNormalRegeneration;
|
|
722
777
|
/**
|
|
723
778
|
* Rebase cleanly applied patches so they are relative to the current generation.
|
|
724
779
|
* This prevents recurring conflicts on subsequent regenerations.
|
|
725
780
|
* Returns the number of patches rebased.
|
|
781
|
+
*
|
|
782
|
+
* @internal Used by Flow implementations in `src/flows/`.
|
|
726
783
|
*/
|
|
727
|
-
|
|
784
|
+
rebasePatches(results: ReplayResult[], currentGenSha: string): Promise<{
|
|
785
|
+
absorbed: number;
|
|
786
|
+
repointed: number;
|
|
787
|
+
contentRebased: number;
|
|
788
|
+
keptAsUserOwned: number;
|
|
789
|
+
absorbedPatchIds: Set<string>;
|
|
790
|
+
}>;
|
|
728
791
|
/**
|
|
729
792
|
* Read THEIRS snapshot (post-customer-edit content) for a list of files
|
|
730
793
|
* from the working tree on disk. Used by `rebasePatches` after a clean
|
|
@@ -735,17 +798,28 @@ declare class ReplayService {
|
|
|
735
798
|
*/
|
|
736
799
|
private readSnapshotFromDisk;
|
|
737
800
|
/**
|
|
738
|
-
*
|
|
739
|
-
* `fromSha` and `toSha` touched any
|
|
740
|
-
*
|
|
741
|
-
*
|
|
801
|
+
* Detects autoversion contamination — returns true when a
|
|
802
|
+
* `[fern-autoversion]` commit between `fromSha` and `toSha` touched any
|
|
803
|
+
* of the patch's `files`. Re-classifies each grep match via
|
|
804
|
+
* `isGenerationCommit` so a customer commit that merely quotes the
|
|
805
|
+
* marker in its body doesn't false-positive.
|
|
806
|
+
*
|
|
807
|
+
* Used by preGenerationRebase to skip the customer-content rebuild for
|
|
808
|
+
* patches whose files were last touched by an autoversion step — a
|
|
809
|
+
* naive `git diff currentGen HEAD` there would capture the
|
|
810
|
+
* placeholder→semver swap as customer customization.
|
|
742
811
|
*/
|
|
743
812
|
private hasAutoversionContamination;
|
|
744
813
|
/**
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
814
|
+
* Rebuild patch_content from `theirs_snapshot` (captured at detection
|
|
815
|
+
* time, predates pipeline content) rather than a HEAD-relative diff.
|
|
816
|
+
* Returns `null` when snapshot is missing or doesn't cover every file
|
|
817
|
+
* in `patch.files` — caller leaves the patch unchanged.
|
|
818
|
+
*
|
|
819
|
+
* Used as the autoversion-contamination escape hatch: when the
|
|
820
|
+
* HEAD-relative diff would capture pipeline content (autoversion's
|
|
821
|
+
* placeholder swap) as customer customization, the snapshot path
|
|
822
|
+
* preserves the customer's actual intent.
|
|
749
823
|
*/
|
|
750
824
|
private computeSnapshotBasedPatchDiff;
|
|
751
825
|
/**
|
|
@@ -764,7 +838,7 @@ declare class ReplayService {
|
|
|
764
838
|
* **Skips patches sharing files with other patches.** HEAD's content
|
|
765
839
|
* for a file shared by N patches is cumulative across all of them, so
|
|
766
840
|
* a HEAD-fallback would falsely encode other patches' contributions
|
|
767
|
-
* into one patch's snapshot — breaking
|
|
841
|
+
* into one patch's snapshot — breaking cross-patch isolation
|
|
768
842
|
* if the snapshot fallback later fires. Per-patch snapshots only
|
|
769
843
|
* make sense when the patch owns its file. Multi-patch-same-file
|
|
770
844
|
* legacy lockfiles keep using line-anchored reconstruction (the
|
|
@@ -778,37 +852,6 @@ declare class ReplayService {
|
|
|
778
852
|
* Skips patches that already have a snapshot.
|
|
779
853
|
*/
|
|
780
854
|
private migratePatchesToSnapshot;
|
|
781
|
-
/**
|
|
782
|
-
* Determine whether `file` is user-owned (never produced by the generator).
|
|
783
|
-
*
|
|
784
|
-
* Resolution order (first match wins):
|
|
785
|
-
* 1. `patch.user_owned === true` — authoritative, persisted across cycles.
|
|
786
|
-
* 2. `.fernignore` itself, or file matches a .fernignore pattern.
|
|
787
|
-
* 3. Legacy-safe signal: `patch_content` shows this file as a `--- /dev/null`
|
|
788
|
-
* creation. Works for patches that predate the `user_owned` field or
|
|
789
|
-
* whose flag was lost. Immune to rename tricks — relies on raw patch text.
|
|
790
|
-
* 4. Tree-based check against `patch.base_generation` when it is reachable
|
|
791
|
-
* AND distinct from `currentGenSha`. If the base tree is unreachable
|
|
792
|
-
* (shallow clone / aggressive `git gc --prune`), emit a one-time
|
|
793
|
-
* customer-actionable warning (deduplicated via warnedGens) and
|
|
794
|
-
* conservatively treat as user-owned — strictly safer than silent
|
|
795
|
-
* absorption (FER-9809).
|
|
796
|
-
* 5. Final fallback — check `currentGenSha` when there is no usable base
|
|
797
|
-
* (legacy one-gen lockfile). Preserves pre-fix behavior for that edge.
|
|
798
|
-
*
|
|
799
|
-
* `originalFileName` is the pre-rename path when the generator moved the
|
|
800
|
-
* file between the patch's base and the current generation. Tree lookups
|
|
801
|
-
* at ancestor generations use the original path — that's where the
|
|
802
|
-
* generator produced the content.
|
|
803
|
-
*/
|
|
804
|
-
private isFileUserOwned;
|
|
805
|
-
/**
|
|
806
|
-
* Returns true if `file`'s diff section in `patchContent` starts with
|
|
807
|
-
* `--- /dev/null`, meaning this file was introduced by the patch (user
|
|
808
|
-
* creation). Used as a legacy-safe fallback when `patch.user_owned` is
|
|
809
|
-
* absent or cleared.
|
|
810
|
-
*/
|
|
811
|
-
private fileIsCreationInPatchContent;
|
|
812
855
|
/**
|
|
813
856
|
* For conflict patches with mixed results (some files merged, some conflicted),
|
|
814
857
|
* check if the cleanly merged files were absorbed by the generator (empty diff).
|
|
@@ -823,7 +866,12 @@ declare class ReplayService {
|
|
|
823
866
|
* Pre-generation rebase: update patches using the customer's current state.
|
|
824
867
|
* Called BEFORE commitGeneration() while HEAD has customer code.
|
|
825
868
|
*/
|
|
826
|
-
|
|
869
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
870
|
+
preGenerationRebase(patches: StoredPatch[]): Promise<{
|
|
871
|
+
conflictResolved: number;
|
|
872
|
+
conflictAbsorbed: number;
|
|
873
|
+
contentRefreshed: number;
|
|
874
|
+
}>;
|
|
827
875
|
/**
|
|
828
876
|
* After applyPatches(), surface a warning for each (patch × file) where
|
|
829
877
|
* diff3 silently dropped lines that were unchanged context in the
|
|
@@ -832,21 +880,35 @@ declare class ReplayService {
|
|
|
832
880
|
* relied on them. This is the "surface or preserve, never silently drop"
|
|
833
881
|
* contract for legitimate-deletion cases.
|
|
834
882
|
*/
|
|
835
|
-
|
|
883
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
884
|
+
recordDroppedContextLineWarnings(results: ReplayResult[]): void;
|
|
836
885
|
/**
|
|
837
886
|
* After applyPatches(), strip conflict markers from conflicting files
|
|
838
887
|
* so only clean content is committed. Keeps the Generated (OURS) side.
|
|
839
888
|
*/
|
|
840
|
-
|
|
889
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
890
|
+
revertConflictingFiles(results: ReplayResult[]): void;
|
|
841
891
|
/**
|
|
842
892
|
* Clean up stale conflict markers left by a previous crashed run.
|
|
843
893
|
* Called after commitGeneration() when HEAD is the [fern-generated] commit.
|
|
844
894
|
* Restores files to their clean generated state from HEAD.
|
|
845
895
|
* Skips .fernignore-protected files to prevent overwriting user content.
|
|
846
896
|
*/
|
|
847
|
-
|
|
897
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
898
|
+
cleanupStaleConflictMarkers(): Promise<void>;
|
|
848
899
|
private readFernignorePatterns;
|
|
849
|
-
|
|
900
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
901
|
+
buildReport(flow: "first-generation" | "no-patches" | "normal-regeneration" | "skip-application", patches: StoredPatch[], results: ReplayResult[], options?: ReplayOptions, warnings?: string[], rebaseCounts?: {
|
|
902
|
+
absorbed: number;
|
|
903
|
+
repointed: number;
|
|
904
|
+
contentRebased: number;
|
|
905
|
+
keptAsUserOwned: number;
|
|
906
|
+
absorbedPatchIds: Set<string>;
|
|
907
|
+
}, preRebaseCounts?: {
|
|
908
|
+
conflictResolved: number;
|
|
909
|
+
conflictAbsorbed: number;
|
|
910
|
+
contentRefreshed: number;
|
|
911
|
+
}, patchesReverted?: number): ReplayReport;
|
|
850
912
|
}
|
|
851
913
|
|
|
852
914
|
interface MigrationAnalysis {
|
|
@@ -1053,4 +1115,4 @@ interface StatusGeneration {
|
|
|
1053
1115
|
}
|
|
1054
1116
|
declare function status(outputDir: string): StatusResult;
|
|
1055
1117
|
|
|
1056
|
-
export { type ApplyOptions, type BootstrapOptions, type BootstrapResult, CREDENTIAL_PATTERNS, type CommitInfo, type CommitOptions, type ConflictDetail, type ConflictMetadata, type ConflictReason, type ConflictRegion, type CredentialPattern, type CustomizationsConfig, type DetectionResult, type DiffStat, FERN_BOT_EMAIL, FERN_BOT_LOGIN, FERN_BOT_NAME, FernignoreMigrator, type FileResult, type ForgetOptions, type ForgetResult, type GenerationLock, type GenerationRecord, GitClient, LockfileManager, LockfileNotFoundError, type MatchedPatch, type MergeResult, type MigrationAnalysis, type MigrationResult, type MoveDeclaration, ReplayApplicator, ReplayCommitter, type ReplayConfig, ReplayDetector, type ReplayOptions, type ReplayPreparation, type ReplayReport, type ReplayResult, ReplayService, type ResetOptions, type ResetResult, type ResolveOptions, type ResolveResult, SENSITIVE_FILE_PATTERNS, type StatusGeneration, type StatusPatch, type StatusResult, type StoredPatch, type UnresolvedPatchInfo, bootstrap, forget, isGenerationCommit, isReplayCommit, isRevertCommit, isSensitiveFile, parseRevertedMessage, parseRevertedSha, reset, resolve, scanForCredentials, status, threeWayMerge };
|
|
1118
|
+
export { type ApplyOptions, type BootstrapOptions, type BootstrapResult, CREDENTIAL_PATTERNS, type CommitInfo, type CommitOptions, type ConflictDetail, type ConflictMetadata, type ConflictReason, type ConflictRegion, type CredentialPattern, type CustomizationsConfig, type DetectionResult, type DiffStat, FERN_BOT_EMAIL, FERN_BOT_LOGIN, FERN_BOT_NAME, FernignoreMigrator, type FileResult, type FlowKind, type ForgetOptions, type ForgetResult, type GenerationLock, type GenerationRecord, GitClient, LockfileManager, LockfileNotFoundError, type MatchedPatch, type MergeResult, type MigrationAnalysis, type MigrationResult, type MoveDeclaration, ReplayApplicator, ReplayCommitter, type ReplayConfig, ReplayDetector, type ReplayOptions, type ReplayPreparation, type ReplayReport, type ReplayResult, ReplayService, type ResetOptions, type ResetResult, type ResolveOptions, type ResolveResult, SENSITIVE_FILE_PATTERNS, type StatusGeneration, type StatusPatch, type StatusResult, type StoredPatch, type UnresolvedPatchInfo, bootstrap, forget, isGenerationCommit, isReplayCommit, isRevertCommit, isSensitiveFile, parseRevertedMessage, parseRevertedSha, reset, resolve, scanForCredentials, status, threeWayMerge };
|