@fern-api/fern-api-dev 5.51.1 → 5.52.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.
Files changed (2) hide show
  1. package/cli.cjs +502 -133
  2. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -116508,6 +116508,7 @@ __export(DocsYmlSchemas_exports, {
116508
116508
  BodyThemeConfig: () => BodyThemeConfig,
116509
116509
  ChangelogConfiguration: () => ChangelogConfiguration,
116510
116510
  ChangelogFolderRelativePath: () => ChangelogFolderRelativePath,
116511
+ ChangelogLayout: () => ChangelogLayout,
116511
116512
  CheckConfig: () => CheckConfig,
116512
116513
  CheckRuleSeverity: () => CheckRuleSeverity,
116513
116514
  CheckRulesConfig: () => CheckRulesConfig,
@@ -116634,7 +116635,7 @@ function normalizeTranslationConfig(config5) {
116634
116635
  }
116635
116636
  return config5;
116636
116637
  }
116637
- var TabId, ChangelogFolderRelativePath, AudienceId, RoleId, LibraryName, ProgrammingLanguage, Language, PageActionOption, AIChatLocation, AIChatModel, EditThisPageLaunch, Availability2, VersionAvailability, TitleSource, LibraryLanguage, FontStyle, FontDisplay, SearchbarPlacement, TabsPlacement, SwitcherPlacement, ContentAlignment, HeaderPosition, ProductSwitcherThemeConfig, LanguageSwitcherThemeConfig, FooterNavThemeConfig, TabsThemeConfig, BodyThemeConfig, SidebarThemeConfig, PageActionsThemeConfig, HttpSnippetLanguage, Target, TwitterCardSetting, JsScriptStrategy, FontWeight, ColorThemedConfig, ColorConfig, Audience, Role, CustomDomain, HttpSnippetsConfig, BackgroundImageThemedConfig, BackgroundImageConfiguration, CssConfig, CollapsedStringValue, CollapsedValue, WithPermissions, FeatureFlag, FeatureFlagConfiguration, WithFeatureFlags, WithViewers, SegmentConfig, FullStoryAnalyticsConfig, IntercomConfig, PostHogConfig, GTMConfig, GoogleAnalytics4Config, AnalyticsConfig, AiExamplesConfig, AIChatWebsiteDatasource, AIChatDatasource, PageDescriptionSource, AgentsConfig, AIChatConfig, FontConfigVariant, FontConfigPath, FontConfig, DocsTypographyConfig, ThemeConfig, LayoutConfig, SearchSettingsConfig, DocsSettingsConfig, ColorsConfiguration, CustomPageAction, PageActionOptions, PageActionsConfig, GithubEditThisPageConfig, EditThisPageConfig, DocsInstance, LogoConfiguration, LinkConfiguration, VersionedSnippetLanguageConfiguration, SnippetLanguageConfiguration, SnippetsConfiguration, PlaygroundButtonSettings, PlaygroundSettings, AnnouncementConfig, NavbarLinkConfig, NavbarGithubConfigWithOptions, NavbarGithubConfig, NavbarDropdownConfig, NavbarLink, FooterLinksConfig, JsFileConfigSettings, JsFileConfig, JsRemoteConfig, JsConfigOptions, JsConfig, MetadataConfig, RedirectConfig, CheckRuleSeverity, CheckRulesConfig, CheckConfig, IntegrationsConfig, ExperimentalConfig, GitLibraryInputSchema, PathLibraryInputSchema, LibraryInputConfiguration, LibraryOutputConfiguration, LibraryConfiguration, PageConfiguration, ChangelogConfiguration, LibraryReferenceConfiguration, FolderConfiguration, ApiReferenceEndpointConfiguration, ApiReferenceOperationConfiguration, ApiReferenceLayoutItem, ApiReferenceSectionConfiguration, ApiReferencePackageConfigurationWithOptions, ApiReferencePackageConfiguration, ApiReferenceConfiguration, NavigationItem, SectionConfiguration, UntabbedNavigationConfig, TabVariant, TabbedNavigationItemWithLayout, TabbedNavigationItemWithVariants, TabbedNavigationItem, TabbedNavigationConfig, NavigationConfig, TabConfig, VersionConfig, VersionFileConfig, ProductConfigBase, InternalProduct, ExternalProduct, ProductConfig, RelativeProductPath, AbsoluteProductPath, ProductPath, ProductFileConfig, TranslationConfigObject, TranslationConfig, DocsConfiguration;
116638
+ var TabId, ChangelogFolderRelativePath, AudienceId, RoleId, LibraryName, ProgrammingLanguage, Language, PageActionOption, AIChatLocation, AIChatModel, EditThisPageLaunch, Availability2, VersionAvailability, TitleSource, LibraryLanguage, FontStyle, FontDisplay, SearchbarPlacement, TabsPlacement, SwitcherPlacement, ContentAlignment, HeaderPosition, ChangelogLayout, ProductSwitcherThemeConfig, LanguageSwitcherThemeConfig, FooterNavThemeConfig, TabsThemeConfig, BodyThemeConfig, SidebarThemeConfig, PageActionsThemeConfig, HttpSnippetLanguage, Target, TwitterCardSetting, JsScriptStrategy, FontWeight, ColorThemedConfig, ColorConfig, Audience, Role, CustomDomain, HttpSnippetsConfig, BackgroundImageThemedConfig, BackgroundImageConfiguration, CssConfig, CollapsedStringValue, CollapsedValue, WithPermissions, FeatureFlag, FeatureFlagConfiguration, WithFeatureFlags, WithViewers, SegmentConfig, FullStoryAnalyticsConfig, IntercomConfig, PostHogConfig, GTMConfig, GoogleAnalytics4Config, AnalyticsConfig, AiExamplesConfig, AIChatWebsiteDatasource, AIChatDatasource, PageDescriptionSource, AgentsConfig, AIChatConfig, FontConfigVariant, FontConfigPath, FontConfig, DocsTypographyConfig, ThemeConfig, LayoutConfig, SearchSettingsConfig, DocsSettingsConfig, ColorsConfiguration, CustomPageAction, PageActionOptions, PageActionsConfig, GithubEditThisPageConfig, EditThisPageConfig, DocsInstance, LogoConfiguration, LinkConfiguration, VersionedSnippetLanguageConfiguration, SnippetLanguageConfiguration, SnippetsConfiguration, PlaygroundButtonSettings, PlaygroundSettings, AnnouncementConfig, NavbarLinkConfig, NavbarGithubConfigWithOptions, NavbarGithubConfig, NavbarDropdownConfig, NavbarLink, FooterLinksConfig, JsFileConfigSettings, JsFileConfig, JsRemoteConfig, JsConfigOptions, JsConfig, MetadataConfig, RedirectConfig, CheckRuleSeverity, CheckRulesConfig, CheckConfig, IntegrationsConfig, ExperimentalConfig, GitLibraryInputSchema, PathLibraryInputSchema, LibraryInputConfiguration, LibraryOutputConfiguration, LibraryConfiguration, PageConfiguration, ChangelogConfiguration, LibraryReferenceConfiguration, FolderConfiguration, ApiReferenceEndpointConfiguration, ApiReferenceOperationConfiguration, ApiReferenceLayoutItem, ApiReferenceSectionConfiguration, ApiReferencePackageConfigurationWithOptions, ApiReferencePackageConfiguration, ApiReferenceConfiguration, NavigationItem, SectionConfiguration, UntabbedNavigationConfig, TabVariant, TabbedNavigationItemWithLayout, TabbedNavigationItemWithVariants, TabbedNavigationItem, TabbedNavigationConfig, NavigationConfig, TabConfig, VersionConfig, VersionFileConfig, ProductConfigBase, InternalProduct, ExternalProduct, ProductConfig, RelativeProductPath, AbsoluteProductPath, ProductPath, ProductFileConfig, TranslationConfigObject, TranslationConfig, DocsConfiguration;
116638
116639
  var init_DocsYmlSchemas = __esm({
116639
116640
  "../configuration/lib/docs-yml/DocsYmlSchemas.js"() {
116640
116641
  "use strict";
@@ -116694,6 +116695,7 @@ var init_DocsYmlSchemas = __esm({
116694
116695
  SwitcherPlacement = external_exports.enum(["header", "sidebar"]);
116695
116696
  ContentAlignment = external_exports.enum(["center", "left"]);
116696
116697
  HeaderPosition = external_exports.enum(["fixed", "static"]);
116698
+ ChangelogLayout = external_exports.enum(["timeline", "classic"]);
116697
116699
  ProductSwitcherThemeConfig = external_exports.enum(["default", "toggle", "tabs"]);
116698
116700
  LanguageSwitcherThemeConfig = external_exports.enum(["default", "minimal"]);
116699
116701
  FooterNavThemeConfig = external_exports.enum(["default", "minimal"]);
@@ -116843,7 +116845,8 @@ var init_DocsYmlSchemas = __esm({
116843
116845
  "disable-header": external_exports.boolean().optional(),
116844
116846
  "hide-nav-links": external_exports.boolean().optional(),
116845
116847
  "hide-feedback": external_exports.boolean().optional(),
116846
- "mobile-toc": external_exports.boolean().optional()
116848
+ "mobile-toc": external_exports.boolean().optional(),
116849
+ "changelog-layout": ChangelogLayout.optional()
116847
116850
  });
116848
116851
  SearchSettingsConfig = external_exports.object({
116849
116852
  "prioritize-current-product": external_exports.boolean().optional(),
@@ -117580,6 +117583,18 @@ var init_ChangelogFolderRelativePath = __esm({
117580
117583
  }
117581
117584
  });
117582
117585
 
117586
+ // ../configuration/lib/docs-yml/schemas/sdk/api/resources/docs/types/ChangelogLayout.js
117587
+ var ChangelogLayout2;
117588
+ var init_ChangelogLayout = __esm({
117589
+ "../configuration/lib/docs-yml/schemas/sdk/api/resources/docs/types/ChangelogLayout.js"() {
117590
+ "use strict";
117591
+ ChangelogLayout2 = {
117592
+ Timeline: "timeline",
117593
+ Classic: "classic"
117594
+ };
117595
+ }
117596
+ });
117597
+
117583
117598
  // ../configuration/lib/docs-yml/schemas/sdk/api/resources/docs/types/CheckConfig.js
117584
117599
  var init_CheckConfig = __esm({
117585
117600
  "../configuration/lib/docs-yml/schemas/sdk/api/resources/docs/types/CheckConfig.js"() {
@@ -118689,6 +118704,7 @@ var init_types96 = __esm({
118689
118704
  init_BodyThemeConfig();
118690
118705
  init_ChangelogConfiguration();
118691
118706
  init_ChangelogFolderRelativePath();
118707
+ init_ChangelogLayout();
118692
118708
  init_CheckConfig();
118693
118709
  init_CheckRuleSeverity();
118694
118710
  init_CheckRulesConfig();
@@ -118826,6 +118842,7 @@ __export(docs_exports, {
118826
118842
  AiChatModel: () => AiChatModel,
118827
118843
  Availability: () => Availability3,
118828
118844
  BodyThemeConfig: () => BodyThemeConfig2,
118845
+ ChangelogLayout: () => ChangelogLayout2,
118829
118846
  CheckRuleSeverity: () => CheckRuleSeverity2,
118830
118847
  CollapsedStringValue: () => CollapsedStringValue2,
118831
118848
  ContentAlignment: () => ContentAlignment2,
@@ -118879,6 +118896,7 @@ __export(api_exports2, {
118879
118896
  AiChatModel: () => AiChatModel,
118880
118897
  Availability: () => Availability3,
118881
118898
  BodyThemeConfig: () => BodyThemeConfig2,
118899
+ ChangelogLayout: () => ChangelogLayout2,
118882
118900
  CheckRuleSeverity: () => CheckRuleSeverity2,
118883
118901
  CollapsedStringValue: () => CollapsedStringValue2,
118884
118902
  ContentAlignment: () => ContentAlignment2,
@@ -121801,6 +121819,16 @@ var init_ChangelogConfiguration2 = __esm({
121801
121819
  }
121802
121820
  });
121803
121821
 
121822
+ // ../configuration/lib/docs-yml/schemas/sdk/serialization/resources/docs/types/ChangelogLayout.js
121823
+ var ChangelogLayout3;
121824
+ var init_ChangelogLayout2 = __esm({
121825
+ "../configuration/lib/docs-yml/schemas/sdk/serialization/resources/docs/types/ChangelogLayout.js"() {
121826
+ "use strict";
121827
+ init_core5();
121828
+ ChangelogLayout3 = schemas_exports6.enum_(["timeline", "classic"]);
121829
+ }
121830
+ });
121831
+
121804
121832
  // ../configuration/lib/docs-yml/schemas/sdk/serialization/resources/docs/types/CheckRuleSeverity.js
121805
121833
  var CheckRuleSeverity3;
121806
121834
  var init_CheckRuleSeverity2 = __esm({
@@ -122388,6 +122416,7 @@ var init_LayoutConfig2 = __esm({
122388
122416
  "../configuration/lib/docs-yml/schemas/sdk/serialization/resources/docs/types/LayoutConfig.js"() {
122389
122417
  "use strict";
122390
122418
  init_core5();
122419
+ init_ChangelogLayout2();
122391
122420
  init_ContentAlignment2();
122392
122421
  init_HeaderPosition2();
122393
122422
  init_SearchbarPlacement2();
@@ -122406,7 +122435,8 @@ var init_LayoutConfig2 = __esm({
122406
122435
  disableHeader: schemas_exports6.property("disable-header", schemas_exports6.boolean().optional()),
122407
122436
  hideNavLinks: schemas_exports6.property("hide-nav-links", schemas_exports6.boolean().optional()),
122408
122437
  hideFeedback: schemas_exports6.property("hide-feedback", schemas_exports6.boolean().optional()),
122409
- mobileToc: schemas_exports6.property("mobile-toc", schemas_exports6.boolean().optional())
122438
+ mobileToc: schemas_exports6.property("mobile-toc", schemas_exports6.boolean().optional()),
122439
+ changelogLayout: schemas_exports6.property("changelog-layout", ChangelogLayout3.optional())
122410
122440
  });
122411
122441
  }
122412
122442
  });
@@ -123486,6 +123516,7 @@ var init_types97 = __esm({
123486
123516
  init_BodyThemeConfig2();
123487
123517
  init_ChangelogConfiguration2();
123488
123518
  init_ChangelogFolderRelativePath2();
123519
+ init_ChangelogLayout2();
123489
123520
  init_CheckConfig2();
123490
123521
  init_CheckRuleSeverity2();
123491
123522
  init_CheckRulesConfig2();
@@ -123644,6 +123675,7 @@ __export(docs_exports2, {
123644
123675
  BodyThemeConfig: () => BodyThemeConfig3,
123645
123676
  ChangelogConfiguration: () => ChangelogConfiguration2,
123646
123677
  ChangelogFolderRelativePath: () => ChangelogFolderRelativePath2,
123678
+ ChangelogLayout: () => ChangelogLayout3,
123647
123679
  CheckConfig: () => CheckConfig2,
123648
123680
  CheckRuleSeverity: () => CheckRuleSeverity3,
123649
123681
  CheckRulesConfig: () => CheckRulesConfig2,
@@ -123816,6 +123848,7 @@ __export(serialization_exports3, {
123816
123848
  BodyThemeConfig: () => BodyThemeConfig3,
123817
123849
  ChangelogConfiguration: () => ChangelogConfiguration2,
123818
123850
  ChangelogFolderRelativePath: () => ChangelogFolderRelativePath2,
123851
+ ChangelogLayout: () => ChangelogLayout3,
123819
123852
  CheckConfig: () => CheckConfig2,
123820
123853
  CheckRuleSeverity: () => CheckRuleSeverity3,
123821
123854
  CheckRulesConfig: () => CheckRulesConfig2,
@@ -123970,6 +124003,7 @@ __export(schemas_exports7, {
123970
124003
  AiChatModel: () => AiChatModel,
123971
124004
  Availability: () => Availability3,
123972
124005
  BodyThemeConfig: () => BodyThemeConfig2,
124006
+ ChangelogLayout: () => ChangelogLayout2,
123973
124007
  CheckRuleSeverity: () => CheckRuleSeverity2,
123974
124008
  CollapsedStringValue: () => CollapsedStringValue2,
123975
124009
  ContentAlignment: () => ContentAlignment2,
@@ -153498,6 +153532,9 @@ function convertSchemaObject(schema, wrapAsOptional, wrapAsNullable, context3, b
153498
153532
  let groupName = (typeof mixedGroupName === "string" ? [mixedGroupName] : mixedGroupName) ?? [];
153499
153533
  groupName = context3.resolveGroupName(groupName);
153500
153534
  const generatedName = getGeneratedTypeName(breadcrumbs, context3.options.preserveSchemaIds);
153535
+ if (nameOverride != null) {
153536
+ breadcrumbs = [nameOverride];
153537
+ }
153501
153538
  const title2 = schema.title;
153502
153539
  const description = schema.description;
153503
153540
  const availability = convertAvailability2(schema);
@@ -232194,6 +232231,11 @@ function convertLayoutConfig(layout, themeTabsAlignment, themeTabsPlacement) {
232194
232231
  hideNavLinks: layout.hideNavLinks ?? false,
232195
232232
  hideFeedback: layout.hideFeedback ?? false,
232196
232233
  mobileToc: layout.mobileToc ?? false,
232234
+ // Passed through as-is (no default): omitted renders the searchable
232235
+ // timeline, "classic" renders the legacy stacked layout. Resolved by the
232236
+ // fern-platform companion PR. Part of the `as unknown as` cast below
232237
+ // until the published FDR SDK adds `changelogLayout`.
232238
+ changelogLayout: layout.changelogLayout,
232197
232239
  tabsAlignment: resolvedTabsAlignment
232198
232240
  };
232199
232241
  }
@@ -569721,9 +569763,9 @@ var require_diff3 = __commonJS({
569721
569763
  }
569722
569764
  });
569723
569765
 
569724
- // ../../../node_modules/.pnpm/@fern-api+replay@0.18.0/node_modules/@fern-api/replay/dist/index.cjs
569766
+ // ../../../node_modules/.pnpm/@fern-api+replay@0.19.0/node_modules/@fern-api/replay/dist/index.cjs
569725
569767
  var require_dist24 = __commonJS({
569726
- "../../../node_modules/.pnpm/@fern-api+replay@0.18.0/node_modules/@fern-api/replay/dist/index.cjs"(exports2, module4) {
569768
+ "../../../node_modules/.pnpm/@fern-api+replay@0.19.0/node_modules/@fern-api/replay/dist/index.cjs"(exports2, module4) {
569727
569769
  "use strict";
569728
569770
  var __defProp5 = Object.defineProperty;
569729
569771
  var __getOwnPropDesc4 = Object.getOwnPropertyDescriptor;
@@ -570141,15 +570183,15 @@ var require_dist24 = __commonJS({
570141
570183
  async function applyPatchToContent(base4, patchContent, filePath) {
570142
570184
  const fileDiff = extractFileDiff(patchContent, filePath);
570143
570185
  if (!fileDiff) return null;
570144
- const tempDir2 = await (0, import_promises510.mkdtemp)((0, import_node_path72.join)((0, import_node_os32.tmpdir)(), "replay-migrate-"));
570186
+ const tempDir2 = await (0, import_promises510.mkdtemp)((0, import_node_path82.join)((0, import_node_os32.tmpdir)(), "replay-migrate-"));
570145
570187
  const tempGit = new GitClient(tempDir2);
570146
570188
  try {
570147
570189
  await tempGit.exec(["init"]);
570148
570190
  await tempGit.exec(["config", "user.email", "migrate@fern.com"]);
570149
570191
  await tempGit.exec(["config", "user.name", "Fern Replay Migration"]);
570150
570192
  await tempGit.exec(["config", "commit.gpgSign", "false"]);
570151
- const tempFilePath = (0, import_node_path72.join)(tempDir2, filePath);
570152
- await (0, import_promises510.mkdir)((0, import_node_path72.dirname)(tempFilePath), { recursive: true });
570193
+ const tempFilePath = (0, import_node_path82.join)(tempDir2, filePath);
570194
+ await (0, import_promises510.mkdir)((0, import_node_path82.dirname)(tempFilePath), { recursive: true });
570153
570195
  await (0, import_promises510.writeFile)(tempFilePath, base4);
570154
570196
  await tempGit.exec(["add", filePath]);
570155
570197
  await tempGit.exec(["commit", "-m", `base for ${filePath}`, "--allow-empty"]);
@@ -570182,13 +570224,13 @@ var require_dist24 = __commonJS({
570182
570224
  }
570183
570225
  var import_promises510;
570184
570226
  var import_node_os32;
570185
- var import_node_path72;
570227
+ var import_node_path82;
570186
570228
  var init_PatchApplyTheirs = __esm3({
570187
570229
  "src/PatchApplyTheirs.ts"() {
570188
570230
  "use strict";
570189
570231
  import_promises510 = require("fs/promises");
570190
570232
  import_node_os32 = require("os");
570191
- import_node_path72 = require("path");
570233
+ import_node_path82 = require("path");
570192
570234
  init_GitClient();
570193
570235
  }
570194
570236
  });
@@ -570453,6 +570495,22 @@ var require_dist24 = __commonJS({
570453
570495
  this.lock.forgotten_hashes.push(hash5);
570454
570496
  }
570455
570497
  }
570498
+ /**
570499
+ * Record a dismissed customization: visibility metadata in `dismissals`
570500
+ * plus a tombstone in `forgotten_hashes` for every content hash.
570501
+ * `forgotten_hashes` stays the only list the detector consults; the
570502
+ * metadata exists so `status` can show what was dismissed.
570503
+ */
570504
+ addDismissal(record23) {
570505
+ this.ensureLoaded();
570506
+ if (!this.lock.dismissals) {
570507
+ this.lock.dismissals = [];
570508
+ }
570509
+ this.lock.dismissals.push(record23);
570510
+ for (const hash5 of record23.content_hashes) {
570511
+ this.addForgottenHash(hash5);
570512
+ }
570513
+ }
570456
570514
  getUnresolvedPatches() {
570457
570515
  this.ensureLoaded();
570458
570516
  return this.lock.patches.filter((p6) => p6.status === "unresolved");
@@ -570614,12 +570672,14 @@ var require_dist24 = __commonJS({
570614
570672
  return snapshot;
570615
570673
  }
570616
570674
  function filterInfrastructureAndSensitiveFiles(rawFilesOutput, fernignorePatterns) {
570617
- const allFiles = rawFilesOutput.trim().split("\n").filter(Boolean).filter((f3) => !INFRASTRUCTURE_FILES.has(f3)).filter((f3) => !f3.startsWith(".fern/"));
570675
+ const rawFiles = rawFilesOutput.trim().split("\n").filter(Boolean);
570676
+ const infrastructureFiles = rawFiles.filter((f3) => INFRASTRUCTURE_FILES.has(f3) || f3.startsWith(".fern/"));
570677
+ const allFiles = rawFiles.filter((f3) => !INFRASTRUCTURE_FILES.has(f3) && !f3.startsWith(".fern/"));
570618
570678
  const sensitiveFiles = allFiles.filter((f3) => isSensitiveFile(f3));
570619
570679
  const nonSensitive = allFiles.filter((f3) => !isSensitiveFile(f3));
570620
570680
  const fernignoredFiles = nonSensitive.filter((f3) => matchesFernignorePattern(f3, fernignorePatterns));
570621
570681
  const files = nonSensitive.filter((f3) => !matchesFernignorePattern(f3, fernignorePatterns));
570622
- return { files, sensitiveFiles, fernignoredFiles };
570682
+ return { files, sensitiveFiles, fernignoredFiles, infrastructureFiles };
570623
570683
  }
570624
570684
  var ReplayDetector = class {
570625
570685
  git;
@@ -570627,6 +570687,15 @@ var require_dist24 = __commonJS({
570627
570687
  sdkOutputDir;
570628
570688
  fernignorePatterns;
570629
570689
  warnings = [];
570690
+ /**
570691
+ * Structured degraded-state channel, mirroring `warnings`. Populated when
570692
+ * detection cannot guarantee customizations were carried forward (baseline
570693
+ * unreachable in this clone). Cleared per-run by `ReplayService.prepareReplay`
570694
+ * alongside `warnings`. Merged into `ReplayReport.degradedReasons` by each
570695
+ * flow handler. Degraded is observability only: detection results are
570696
+ * unchanged.
570697
+ */
570698
+ degradedReasons = [];
570630
570699
  constructor(git, lockManager, sdkOutputDir, fernignorePatterns = []) {
570631
570700
  this.git = git;
570632
570701
  this.lockManager = lockManager;
@@ -570697,6 +570766,10 @@ var require_dist24 = __commonJS({
570697
570766
  this.warnings.push(
570698
570767
  `No generation boundary found in git history and the lockfile's recorded generation (${lock.current_generation.slice(0, 7)}) matches no entry in its generation records. Customer customizations may differ from the last known generation and could be lost on the next regeneration. Run \`fern-replay bootstrap --force\` to re-initialize tracking.`
570699
570768
  );
570769
+ this.degradedReasons.push({
570770
+ code: "baseline-unresolvable",
570771
+ message: `The previous generation recorded in the lockfile (${lock.current_generation.slice(0, 7)}) could not be resolved to any reachable baseline during this run. Customizations may not have been carried forward \u2014 review the result before merging.`
570772
+ });
570700
570773
  }
570701
570774
  return { patches: [], revertedPatchIds: [] };
570702
570775
  }
@@ -570705,6 +570778,12 @@ var require_dist24 = __commonJS({
570705
570778
  this.warnings.push(
570706
570779
  `Generation commit ${lastGen.commit_sha.slice(0, 7)} not found in git history. Falling back to tree-diff detection (likely a shallow clone).`
570707
570780
  );
570781
+ if (lock.patches.length > 0) {
570782
+ this.degradedReasons.push({
570783
+ code: "generation-anchor-unreachable",
570784
+ message: `The previous generation (${lastGen.commit_sha.slice(0, 7)}) was not reachable during this run, and ${lock.patches.length} tracked customization(s) could not be verified against it. Customizations may not have been carried forward \u2014 review the result before merging.`
570785
+ });
570786
+ }
570708
570787
  return this.detectPatchesViaTreeDiff(
570709
570788
  lastGen,
570710
570789
  /* commitKnownMissing */
@@ -570744,54 +570823,27 @@ var require_dist24 = __commonJS({
570744
570823
  if (lock.patches.find((p6) => p6.original_commit === commit.sha)) {
570745
570824
  continue;
570746
570825
  }
570747
- const parents = await this.git.getCommitParents(commit.sha);
570748
- let filesOutput;
570749
- try {
570750
- filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
570751
- } catch {
570826
+ const materialized = await this.materializeCommitPatch(commit.sha);
570827
+ if (materialized.sensitiveFiles.length > 0) {
570752
570828
  this.warnings.push(
570753
- `Could not generate patch for commit ${commit.sha.slice(0, 7)} \u2014 it may be unreachable in a shallow clone. Skipping.`
570829
+ `Sensitive file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${materialized.sensitiveFiles.join(", ")}. These files will not be tracked by replay.`
570754
570830
  );
570755
- continue;
570756
570831
  }
570757
- const { files, sensitiveFiles, fernignoredFiles } = filterInfrastructureAndSensitiveFiles(
570758
- filesOutput,
570759
- this.fernignorePatterns
570760
- );
570761
- if (sensitiveFiles.length > 0) {
570832
+ if (materialized.fernignoredFiles.length > 0) {
570762
570833
  this.warnings.push(
570763
- `Sensitive file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${sensitiveFiles.join(", ")}. These files will not be tracked by replay.`
570834
+ `.fernignore-protected file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${materialized.fernignoredFiles.join(", ")}. These files are managed by .fernignore, not Replay.`
570764
570835
  );
570765
570836
  }
570766
- if (fernignoredFiles.length > 0) {
570837
+ if (materialized.status === "unreachable") {
570767
570838
  this.warnings.push(
570768
- `.fernignore-protected file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${fernignoredFiles.join(", ")}. These files are managed by .fernignore, not Replay.`
570839
+ `Could not generate patch for commit ${commit.sha.slice(0, 7)} \u2014 it may be unreachable in a shallow clone. Skipping.`
570769
570840
  );
570770
- }
570771
- if (files.length === 0) {
570772
570841
  continue;
570773
570842
  }
570774
- const wasFiltered = sensitiveFiles.length > 0 || fernignoredFiles.length > 0;
570775
- const parentSha = parents[0];
570776
- let patchContent;
570777
- if (wasFiltered && parentSha) {
570778
- try {
570779
- patchContent = await this.git.exec(["diff", parentSha, commit.sha, "--", ...files]);
570780
- } catch {
570781
- continue;
570782
- }
570783
- if (!patchContent.trim()) continue;
570784
- } else {
570785
- try {
570786
- patchContent = await this.git.formatPatch(commit.sha);
570787
- } catch {
570788
- this.warnings.push(
570789
- `Could not generate patch for commit ${commit.sha.slice(0, 7)} \u2014 it may be unreachable in a shallow clone. Skipping.`
570790
- );
570791
- continue;
570792
- }
570843
+ if (materialized.status === "empty") {
570844
+ continue;
570793
570845
  }
570794
- const contentHash = this.computeContentHash(patchContent);
570846
+ const { files, patchContent, contentHash } = materialized;
570795
570847
  if (lock.patches.find((p6) => p6.content_hash === contentHash) || forgottenHashes.has(contentHash)) {
570796
570848
  continue;
570797
570849
  }
@@ -570868,6 +570920,63 @@ var require_dist24 = __commonJS({
570868
570920
  const filteredPatches = survivingPatches.filter((_5, i5) => !revertIndicesToRemove.has(i5));
570869
570921
  return { patches: filteredPatches, revertedPatchIds: [...revertedPatchIdSet] };
570870
570922
  }
570923
+ /**
570924
+ * Materialize a commit into patch content exactly as detection does.
570925
+ *
570926
+ * Pipeline: `diff-tree --name-only` → filter infrastructure / sensitive /
570927
+ * `.fernignore`d files → scoped `git diff` when files were stripped,
570928
+ * otherwise full `git format-patch` → content hash.
570929
+ *
570930
+ * This is THE definition of a commit's re-emission identity: the content
570931
+ * hash this method returns is what `detectPatchesInRange` will compute if
570932
+ * the commit re-enters the scan range (e.g. after a regen PR is merged
570933
+ * with a merge commit). Callers that tombstone a dismissed patch must use
570934
+ * this hash — the stored `content_hash` drifts every time the patch is
570935
+ * carried forward, so it does not identify the commit on re-detection.
570936
+ *
570937
+ * Never throws; failures are reported through the `status` discriminant.
570938
+ */
570939
+ async materializeCommitPatch(sha) {
570940
+ let parents;
570941
+ let filesOutput;
570942
+ try {
570943
+ parents = await this.git.getCommitParents(sha);
570944
+ filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", sha]);
570945
+ } catch {
570946
+ return { status: "unreachable", sensitiveFiles: [], fernignoredFiles: [] };
570947
+ }
570948
+ const { files, sensitiveFiles, fernignoredFiles, infrastructureFiles } = filterInfrastructureAndSensitiveFiles(filesOutput, this.fernignorePatterns);
570949
+ if (files.length === 0) {
570950
+ return { status: "empty", sensitiveFiles, fernignoredFiles };
570951
+ }
570952
+ const wasFiltered = sensitiveFiles.length > 0 || fernignoredFiles.length > 0 || infrastructureFiles.length > 0;
570953
+ const parentSha = parents[0];
570954
+ let patchContent;
570955
+ if (wasFiltered && parentSha) {
570956
+ try {
570957
+ patchContent = await this.git.exec(["diff", parentSha, sha, "--", ...files]);
570958
+ } catch {
570959
+ return { status: "empty", sensitiveFiles, fernignoredFiles };
570960
+ }
570961
+ if (!patchContent.trim()) {
570962
+ return { status: "empty", sensitiveFiles, fernignoredFiles };
570963
+ }
570964
+ } else {
570965
+ try {
570966
+ patchContent = await this.git.formatPatch(sha);
570967
+ } catch {
570968
+ return { status: "unreachable", sensitiveFiles, fernignoredFiles };
570969
+ }
570970
+ }
570971
+ return {
570972
+ status: "ok",
570973
+ files,
570974
+ sensitiveFiles,
570975
+ fernignoredFiles,
570976
+ patchContent,
570977
+ contentHash: this.computeContentHash(patchContent)
570978
+ };
570979
+ }
570871
570980
  /**
570872
570981
  * Compute content hash for deduplication.
570873
570982
  *
@@ -570972,6 +571081,13 @@ var require_dist24 = __commonJS({
570972
571081
  async detectPatchesViaTreeDiff(lastGen, commitKnownMissing) {
570973
571082
  const diffBase = await this.resolveDiffBase(lastGen, commitKnownMissing);
570974
571083
  if (!diffBase) {
571084
+ this.warnings.push(
571085
+ `Neither the previous generation's commit (${lastGen.commit_sha.slice(0, 7)}) nor its recorded tree was reachable during this run. Falling back to a bounded commit scan, which cannot see customizations older than the clone window. Widen the clone window (e.g. \`git fetch --unshallow\`) and re-run replay to verify customizations were carried forward.`
571086
+ );
571087
+ this.degradedReasons.push({
571088
+ code: "generation-tree-unreachable",
571089
+ message: `The previous generation (${lastGen.commit_sha.slice(0, 7)}) was not reachable during this run \u2014 neither its commit nor its recorded tree. Customizations may not have been carried forward \u2014 review the result before merging, or widen the clone window and re-run replay.`
571090
+ });
570975
571091
  return this.detectPatchesViaCommitScan();
570976
571092
  }
570977
571093
  const lockForGuard = this.lockManager.read();
@@ -571084,10 +571200,7 @@ var require_dist24 = __commonJS({
571084
571200
  continue;
571085
571201
  }
571086
571202
  const filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
571087
- const { files, sensitiveFiles, fernignoredFiles } = filterInfrastructureAndSensitiveFiles(
571088
- filesOutput,
571089
- this.fernignorePatterns
571090
- );
571203
+ const { files, sensitiveFiles, fernignoredFiles, infrastructureFiles } = filterInfrastructureAndSensitiveFiles(filesOutput, this.fernignorePatterns);
571091
571204
  if (sensitiveFiles.length > 0) {
571092
571205
  this.warnings.push(
571093
571206
  `Sensitive file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${sensitiveFiles.join(", ")}. These files will not be tracked by replay.`
@@ -571098,7 +571211,8 @@ var require_dist24 = __commonJS({
571098
571211
  `.fernignore-protected file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${fernignoredFiles.join(", ")}. These files are managed by .fernignore, not Replay.`
571099
571212
  );
571100
571213
  }
571101
- if ((sensitiveFiles.length > 0 || fernignoredFiles.length > 0) && files.length > 0) {
571214
+ const wasFiltered = sensitiveFiles.length > 0 || fernignoredFiles.length > 0 || infrastructureFiles.length > 0;
571215
+ if (wasFiltered && files.length > 0) {
571102
571216
  if (parents.length === 0) {
571103
571217
  continue;
571104
571218
  }
@@ -572348,14 +572462,21 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572348
572462
  return this.git.getTreeHash(commitSha);
572349
572463
  }
572350
572464
  };
572351
- var import_node_fs210 = require("fs");
572465
+ var import_node_fs36 = require("fs");
572352
572466
  var import_promises610 = require("fs/promises");
572353
- var import_node_path82 = require("path");
572467
+ var import_node_path92 = require("path");
572354
572468
  var import_minimatch52 = require_commonjs3();
572355
572469
  init_GitClient();
572470
+ var import_node_fs210 = require("fs");
572471
+ var import_node_path62 = require("path");
572472
+ function readFernignorePatterns(outputDir) {
572473
+ const fernignorePath = (0, import_node_path62.join)(outputDir, ".fernignore");
572474
+ if (!(0, import_node_fs210.existsSync)(fernignorePath)) return [];
572475
+ return (0, import_node_fs210.readFileSync)(fernignorePath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
572476
+ }
572356
572477
  var import_promises410 = require("fs/promises");
572357
572478
  var import_node_os22 = require("os");
572358
- var import_node_path62 = require("path");
572479
+ var import_node_path72 = require("path");
572359
572480
  var import_node_child_process15 = require("child_process");
572360
572481
  init_HybridReconstruction();
572361
572482
  function normalizeHunkBody(hunk) {
@@ -572573,10 +572694,10 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572573
572694
  }
572574
572695
  async function unifiedDiff(beforeContent, afterContent, filePath) {
572575
572696
  if (beforeContent === afterContent) return "";
572576
- const tmp20 = await (0, import_promises410.mkdtemp)((0, import_node_path62.join)((0, import_node_os22.tmpdir)(), "fern-replay-pp-"));
572697
+ const tmp20 = await (0, import_promises410.mkdtemp)((0, import_node_path72.join)((0, import_node_os22.tmpdir)(), "fern-replay-pp-"));
572577
572698
  try {
572578
- const beforePath = (0, import_node_path62.join)(tmp20, "before");
572579
- const afterPath = (0, import_node_path62.join)(tmp20, "after");
572699
+ const beforePath = (0, import_node_path72.join)(tmp20, "before");
572700
+ const afterPath = (0, import_node_path72.join)(tmp20, "after");
572580
572701
  await (0, import_promises410.writeFile)(beforePath, beforeContent, "utf-8");
572581
572702
  await (0, import_promises410.writeFile)(afterPath, afterContent, "utf-8");
572582
572703
  const raw = await runGitDiffNoIndex(beforePath, afterPath);
@@ -572748,6 +572869,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572748
572869
  patchesToApply: [],
572749
572870
  newPatchIds: /* @__PURE__ */ new Set(),
572750
572871
  detectorWarnings: [],
572872
+ detectorDegraded: [],
572751
572873
  revertedCount: 0,
572752
572874
  genSha: genRecord.commit_sha,
572753
572875
  optionsSnapshot: options2
@@ -572805,6 +572927,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572805
572927
  patchesToApply: [],
572806
572928
  newPatchIds: /* @__PURE__ */ new Set(),
572807
572929
  detectorWarnings: [],
572930
+ detectorDegraded: [],
572808
572931
  revertedCount: 0,
572809
572932
  genSha: genRecord.commit_sha,
572810
572933
  optionsSnapshot: options2
@@ -572840,8 +572963,10 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572840
572963
  const previousGenerationSha = preLock.current_generation ?? null;
572841
572964
  let { patches: newPatches, revertedPatchIds } = await this.service.detector.detectNewPatches();
572842
572965
  const detectorWarnings = [...this.service.detector.warnings];
572966
+ const detectorDegraded = [...this.service.detector.degradedReasons];
572843
572967
  if (options2?.dryRun) {
572844
572968
  const dryRunWarnings = [...detectorWarnings, ...this.service.warnings];
572969
+ const dryRunDegraded = dedupeDegradedReasons([...detectorDegraded, ...this.service.degradedReasons]);
572845
572970
  const preparedReport = {
572846
572971
  flow: "no-patches",
572847
572972
  patchesDetected: newPatches.length,
@@ -572851,7 +572976,9 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572851
572976
  patchesReverted: revertedPatchIds.length,
572852
572977
  conflicts: [],
572853
572978
  wouldApply: newPatches,
572854
- warnings: dryRunWarnings.length > 0 ? dryRunWarnings : void 0
572979
+ warnings: dryRunWarnings.length > 0 ? dryRunWarnings : void 0,
572980
+ degraded: dryRunDegraded.length > 0 ? true : void 0,
572981
+ degradedReasons: dryRunDegraded.length > 0 ? dryRunDegraded : void 0
572855
572982
  };
572856
572983
  const headSha = await this.service.git.exec(["rev-parse", "HEAD"]).then((s5) => s5.trim()).catch(() => "");
572857
572984
  return {
@@ -572905,6 +573032,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572905
573032
  patchesToApply: newPatches,
572906
573033
  newPatchIds: new Set(newPatches.map((p6) => p6.id)),
572907
573034
  detectorWarnings,
573035
+ detectorDegraded,
572908
573036
  revertedCount: revertedPatchIds.length,
572909
573037
  genSha: genRecord.commit_sha,
572910
573038
  optionsSnapshot: options2
@@ -572948,6 +573076,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572948
573076
  }
572949
573077
  }
572950
573078
  const warnings = [...detectorWarnings, ...this.service.warnings];
573079
+ const degradedReasons = [...prep._prepared.detectorDegraded, ...this.service.degradedReasons];
572951
573080
  return this.service.buildReport(
572952
573081
  "no-patches",
572953
573082
  newPatches,
@@ -572956,7 +573085,8 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572956
573085
  warnings,
572957
573086
  rebaseCounts,
572958
573087
  void 0,
572959
- prep._prepared.revertedCount
573088
+ prep._prepared.revertedCount,
573089
+ degradedReasons
572960
573090
  );
572961
573091
  }
572962
573092
  };
@@ -572971,6 +573101,10 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572971
573101
  const existingPatches2 = this.service.lockManager.getPatches();
572972
573102
  const { patches: newPatches2, revertedPatchIds: dryRunReverted } = await this.service.detector.detectNewPatches();
572973
573103
  const warnings = [...this.service.detector.warnings, ...this.service.warnings];
573104
+ const dryRunDegraded = dedupeDegradedReasons([
573105
+ ...this.service.detector.degradedReasons,
573106
+ ...this.service.degradedReasons
573107
+ ]);
572974
573108
  const allPatches2 = [...existingPatches2, ...newPatches2];
572975
573109
  const preparedReport = {
572976
573110
  flow: "normal-regeneration",
@@ -572981,7 +573115,9 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
572981
573115
  patchesReverted: dryRunReverted.length,
572982
573116
  conflicts: [],
572983
573117
  wouldApply: allPatches2,
572984
- warnings: warnings.length > 0 ? warnings : void 0
573118
+ warnings: warnings.length > 0 ? warnings : void 0,
573119
+ degraded: dryRunDegraded.length > 0 ? true : void 0,
573120
+ degradedReasons: dryRunDegraded.length > 0 ? dryRunDegraded : void 0
572985
573121
  };
572986
573122
  const headSha = await this.service.git.exec(["rev-parse", "HEAD"]).then((s5) => s5.trim()).catch(() => "");
572987
573123
  return {
@@ -573020,6 +573156,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
573020
573156
  existingPatches = this.service.lockManager.getPatches();
573021
573157
  let { patches: newPatches, revertedPatchIds } = await this.service.detector.detectNewPatches();
573022
573158
  const detectorWarnings = [...this.service.detector.warnings];
573159
+ const detectorDegraded = [...this.service.detector.degradedReasons];
573023
573160
  if (removedByPreRebase.length > 0) {
573024
573161
  const removedOriginalCommits = new Set(removedByPreRebase.map((p6) => p6.original_commit));
573025
573162
  const removedOriginalMessages = new Set(removedByPreRebase.map((p6) => p6.original_message));
@@ -573060,6 +573197,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
573060
573197
  newPatchIds: new Set(newPatches.map((p6) => p6.id)),
573061
573198
  preRebaseCounts,
573062
573199
  detectorWarnings,
573200
+ detectorDegraded,
573063
573201
  revertedCount: revertedPatchIds.length,
573064
573202
  genSha: genRecord.commit_sha,
573065
573203
  optionsSnapshot: options2
@@ -573108,6 +573246,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
573108
573246
  await this.service.committer.commitReplay(appliedCount, allPatches, void 0, { buckets });
573109
573247
  }
573110
573248
  const warnings = [...detectorWarnings, ...this.service.warnings];
573249
+ const degradedReasons = [...prep._prepared.detectorDegraded, ...this.service.degradedReasons];
573111
573250
  return this.service.buildReport(
573112
573251
  "normal-regeneration",
573113
573252
  allPatches,
@@ -573116,7 +573255,8 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
573116
573255
  warnings,
573117
573256
  rebaseCounts,
573118
573257
  prep._prepared.preRebaseCounts,
573119
- prep._prepared.revertedCount
573258
+ prep._prepared.revertedCount,
573259
+ degradedReasons
573120
573260
  );
573121
573261
  }
573122
573262
  };
@@ -573154,6 +573294,16 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
573154
573294
  * @internal Used by Flow implementations in `src/flows/`.
573155
573295
  */
573156
573296
  warnings = [];
573297
+ /**
573298
+ * Service-level structured degraded reasons, mirroring `warnings`.
573299
+ * Populated at apply time (e.g. `recordUnreachableBaseWarnings` when a
573300
+ * patch's base-generation tree is unreachable). Merged with
573301
+ * `detector.degradedReasons` into `ReplayReport.degradedReasons` by each
573302
+ * flow handler. Cleared at the top of `prepareReplay()`.
573303
+ *
573304
+ * @internal Used by Flow implementations in `src/flows/`.
573305
+ */
573306
+ degradedReasons = [];
573157
573307
  /**
573158
573308
  * @internal Test-only accessor.
573159
573309
  * Returns the ReplayApplicator instance used for the last run. Tests use this
@@ -573197,7 +573347,10 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
573197
573347
  async prepareReplay(options2) {
573198
573348
  this.warnings = [];
573199
573349
  this.detector.warnings.length = 0;
573350
+ this.degradedReasons = [];
573351
+ this.detector.degradedReasons.length = 0;
573200
573352
  this.cleanseLegacyFernignoreEntries();
573353
+ this.cleanseInfrastructureFileEntries();
573201
573354
  return this.selectFlow(options2).prepare(options2);
573202
573355
  }
573203
573356
  /**
@@ -573251,6 +573404,50 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
573251
573404
  this.lockManager.save();
573252
573405
  }
573253
573406
  }
573407
+ /**
573408
+ * Migration step: strips infrastructure file diffs (`.fernignore`, `.fern/*`)
573409
+ * from existing patches' `patch_content`. These diffs were bundled by older
573410
+ * detection logic that did not scope `git diff` when only infrastructure files
573411
+ * were stripped. Patches that end up with no surviving diff sections are
573412
+ * removed entirely.
573413
+ *
573414
+ * Idempotent: a second run is a no-op because the first run already removed
573415
+ * all infrastructure sections. Per-patch try/catch ensures isolation.
573416
+ */
573417
+ cleanseInfrastructureFileEntries() {
573418
+ if (!this.lockManager.exists()) return;
573419
+ this.lockManager.read();
573420
+ let lockfileMutated = false;
573421
+ const patches = this.lockManager.getPatches();
573422
+ for (const patch5 of patches) {
573423
+ try {
573424
+ const result = computeInfrastructureFileCleanse(
573425
+ patch5,
573426
+ (content5) => this.detector.computeContentHash(content5)
573427
+ );
573428
+ if (result.unchanged) continue;
573429
+ if (result.remove) {
573430
+ this.lockManager.removePatch(patch5.id);
573431
+ this.warnings.push(
573432
+ `Stripped infrastructure file diffs from patch ${patch5.id}: patch contained only infrastructure file changes (${result.strippedPaths.join(", ")}) \u2014 removed.`
573433
+ );
573434
+ } else {
573435
+ this.lockManager.updatePatch(patch5.id, {
573436
+ patch_content: result.patch_content,
573437
+ content_hash: result.content_hash
573438
+ });
573439
+ this.warnings.push(
573440
+ `Stripped infrastructure file diffs from patch ${patch5.id}: removed bundled diffs for ${result.strippedPaths.join(", ")}.`
573441
+ );
573442
+ }
573443
+ lockfileMutated = true;
573444
+ } catch {
573445
+ }
573446
+ }
573447
+ if (lockfileMutated) {
573448
+ this.lockManager.save();
573449
+ }
573450
+ }
573254
573451
  /**
573255
573452
  * Phase 2 of the two-phase replay flow. Applies patches, rebases them against
573256
573453
  * the generation, saves the lockfile, and commits `[fern-replay]` (or stages
@@ -573506,7 +573703,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
573506
573703
  for (const file4 of files) {
573507
573704
  if (isBinaryFile(file4)) continue;
573508
573705
  try {
573509
- const content5 = await (0, import_promises610.readFile)((0, import_node_path82.join)(this.outputDir, file4), "utf-8");
573706
+ const content5 = await (0, import_promises610.readFile)((0, import_node_path92.join)(this.outputDir, file4), "utf-8");
573510
573707
  snapshot[file4] = content5;
573511
573708
  } catch {
573512
573709
  }
@@ -574023,6 +574220,12 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574023
574220
  );
574024
574221
  }
574025
574222
  }
574223
+ if (warned2.size > 0) {
574224
+ this.degradedReasons.push({
574225
+ code: "patch-base-unreachable",
574226
+ message: `${warned2.size} customization file(s) could not be checked against the generation they were made on because that generation's tree was not reachable during this run. Customizations may not have been carried forward \u2014 review the result before merging, or widen the clone window and re-run replay.`
574227
+ });
574228
+ }
574026
574229
  }
574027
574230
  /**
574028
574231
  * After applyPatches(), strip conflict markers from conflicting files
@@ -574034,11 +574237,11 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574034
574237
  if (result.status !== "conflict" || !result.fileResults) continue;
574035
574238
  for (const fileResult of result.fileResults) {
574036
574239
  if (fileResult.status !== "conflict") continue;
574037
- const filePath = (0, import_node_path82.join)(this.outputDir, fileResult.file);
574240
+ const filePath = (0, import_node_path92.join)(this.outputDir, fileResult.file);
574038
574241
  try {
574039
- const content5 = (0, import_node_fs210.readFileSync)(filePath, "utf-8");
574242
+ const content5 = (0, import_node_fs36.readFileSync)(filePath, "utf-8");
574040
574243
  const stripped = stripConflictMarkers(content5);
574041
- (0, import_node_fs210.writeFileSync)(filePath, stripped);
574244
+ (0, import_node_fs36.writeFileSync)(filePath, stripped);
574042
574245
  } catch {
574043
574246
  }
574044
574247
  }
@@ -574066,12 +574269,11 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574066
574269
  }
574067
574270
  /** @internal Used by Flow implementations in `src/flows/`. */
574068
574271
  readFernignorePatterns() {
574069
- const fernignorePath = (0, import_node_path82.join)(this.outputDir, ".fernignore");
574070
- if (!(0, import_node_fs210.existsSync)(fernignorePath)) return [];
574071
- return (0, import_node_fs210.readFileSync)(fernignorePath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
574272
+ return readFernignorePatterns(this.outputDir);
574072
574273
  }
574073
574274
  /** @internal Used by Flow implementations in `src/flows/`. */
574074
- buildReport(flow4, patches, results, options2, warnings, rebaseCounts, preRebaseCounts, patchesReverted) {
574275
+ buildReport(flow4, patches, results, options2, warnings, rebaseCounts, preRebaseCounts, patchesReverted, degradedReasons) {
574276
+ const degraded = dedupeDegradedReasons(degradedReasons ?? []);
574075
574277
  const conflictResults = results.filter((r4) => r4.status === "conflict");
574076
574278
  const conflictDetails = conflictResults.map((r4) => {
574077
574279
  const conflictFiles = r4.fileResults?.filter((f3) => f3.status === "conflict") ?? [];
@@ -574111,10 +574313,23 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574111
574313
  conflictDetails: r4.fileResults?.filter((f3) => f3.status === "conflict") ?? []
574112
574314
  })) : void 0,
574113
574315
  wouldApply: options2?.dryRun ? patches : void 0,
574114
- warnings: warnings && warnings.length > 0 ? warnings : void 0
574316
+ warnings: warnings && warnings.length > 0 ? warnings : void 0,
574317
+ degraded: degraded.length > 0 ? true : void 0,
574318
+ degradedReasons: degraded.length > 0 ? degraded : void 0
574115
574319
  };
574116
574320
  }
574117
574321
  };
574322
+ function dedupeDegradedReasons(reasons) {
574323
+ const seen = /* @__PURE__ */ new Set();
574324
+ const out = [];
574325
+ for (const reason of reasons) {
574326
+ const key2 = `${reason.code}\0${reason.message}`;
574327
+ if (seen.has(key2)) continue;
574328
+ seen.add(key2);
574329
+ out.push(reason);
574330
+ }
574331
+ return out;
574332
+ }
574118
574333
  function computeCommitBuckets(results, absorbedPatchIds, patchesPassedToCommit) {
574119
574334
  const resultByPatchId = /* @__PURE__ */ new Map();
574120
574335
  for (const r4 of results) resultByPatchId.set(r4.patch.id, r4);
@@ -574163,7 +574378,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574163
574378
  }
574164
574379
  }
574165
574380
  }
574166
- const { stripped: patchContentStripped, content: newPatchContent, strippedSectionPaths } = stripFernignoreDiffSections(patch5.patch_content, matchesFernignore);
574381
+ const { stripped: patchContentStripped, content: newPatchContent, strippedSectionPaths } = stripDiffSectionsByPredicate(patch5.patch_content, matchesFernignore);
574167
574382
  const unchanged = strippedFromFiles.length === 0 && !snapshotChanged && !patchContentStripped;
574168
574383
  if (unchanged) {
574169
574384
  return {
@@ -574198,7 +574413,38 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574198
574413
  strippedPaths
574199
574414
  };
574200
574415
  }
574201
- function stripFernignoreDiffSections(patchContent, matchesFernignore) {
574416
+ function isInfrastructureFile(filePath) {
574417
+ return filePath === ".fernignore" || filePath.startsWith(".fern/");
574418
+ }
574419
+ function computeInfrastructureFileCleanse(patch5, computeContentHash2) {
574420
+ const { stripped, content: newPatchContent, strippedSectionPaths } = stripDiffSectionsByPredicate(patch5.patch_content, isInfrastructureFile);
574421
+ if (!stripped) {
574422
+ return {
574423
+ unchanged: true,
574424
+ remove: false,
574425
+ patch_content: patch5.patch_content,
574426
+ content_hash: patch5.content_hash,
574427
+ strippedPaths: []
574428
+ };
574429
+ }
574430
+ if (newPatchContent.trim() === "" || patch5.files.length === 0) {
574431
+ return {
574432
+ unchanged: false,
574433
+ remove: true,
574434
+ patch_content: "",
574435
+ content_hash: "",
574436
+ strippedPaths: strippedSectionPaths
574437
+ };
574438
+ }
574439
+ return {
574440
+ unchanged: false,
574441
+ remove: false,
574442
+ patch_content: newPatchContent,
574443
+ content_hash: computeContentHash2(newPatchContent),
574444
+ strippedPaths: strippedSectionPaths
574445
+ };
574446
+ }
574447
+ function stripDiffSectionsByPredicate(patchContent, shouldStrip) {
574202
574448
  const lines = patchContent.split("\n");
574203
574449
  const sectionStarts = [];
574204
574450
  for (let i5 = 0; i5 < lines.length; i5++) {
@@ -574220,7 +574466,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574220
574466
  const section = sectionStarts[i5];
574221
574467
  const next2 = sectionStarts[i5 + 1];
574222
574468
  const endIdx = next2 ? next2.idx : lines.length;
574223
- if (section.path !== null && matchesFernignore(section.path)) {
574469
+ if (section.path !== null && shouldStrip(section.path)) {
574224
574470
  stripped = true;
574225
574471
  strippedSectionPaths.push(section.path);
574226
574472
  continue;
@@ -574230,8 +574476,8 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574230
574476
  return { stripped, content: newLines.join("\n"), strippedSectionPaths };
574231
574477
  }
574232
574478
  var import_node_crypto22 = require("crypto");
574233
- var import_node_fs36 = require("fs");
574234
- var import_node_path92 = require("path");
574479
+ var import_node_fs42 = require("fs");
574480
+ var import_node_path102 = require("path");
574235
574481
  var import_minimatch62 = require_commonjs3();
574236
574482
  var import_yaml22 = require_dist11();
574237
574483
  var FernignoreMigrator = class {
@@ -574244,14 +574490,14 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574244
574490
  this.outputDir = outputDir;
574245
574491
  }
574246
574492
  fernignoreExists() {
574247
- return (0, import_node_fs36.existsSync)((0, import_node_path92.join)(this.outputDir, ".fernignore"));
574493
+ return (0, import_node_fs42.existsSync)((0, import_node_path102.join)(this.outputDir, ".fernignore"));
574248
574494
  }
574249
574495
  readFernignorePatterns() {
574250
- const fernignorePath = (0, import_node_path92.join)(this.outputDir, ".fernignore");
574251
- if (!(0, import_node_fs36.existsSync)(fernignorePath)) {
574496
+ const fernignorePath = (0, import_node_path102.join)(this.outputDir, ".fernignore");
574497
+ if (!(0, import_node_fs42.existsSync)(fernignorePath)) {
574252
574498
  return [];
574253
574499
  }
574254
- const content5 = (0, import_node_fs36.readFileSync)(fernignorePath, "utf-8");
574500
+ const content5 = (0, import_node_fs42.readFileSync)(fernignorePath, "utf-8");
574255
574501
  return content5.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
574256
574502
  }
574257
574503
  /** Analyze .fernignore patterns vs git history. Creates synthetic patches for files differing from pristine generation. */
@@ -574356,16 +574602,16 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574356
574602
  return results;
574357
574603
  }
574358
574604
  readFileContent(filePath) {
574359
- const fullPath = (0, import_node_path92.join)(this.outputDir, filePath);
574360
- if (!(0, import_node_fs36.existsSync)(fullPath)) {
574605
+ const fullPath = (0, import_node_path102.join)(this.outputDir, filePath);
574606
+ if (!(0, import_node_fs42.existsSync)(fullPath)) {
574361
574607
  return null;
574362
574608
  }
574363
574609
  try {
574364
- const stat7 = (0, import_node_fs36.statSync)(fullPath);
574610
+ const stat7 = (0, import_node_fs42.statSync)(fullPath);
574365
574611
  if (stat7.isDirectory()) {
574366
574612
  return null;
574367
574613
  }
574368
- return (0, import_node_fs36.readFileSync)(fullPath, "utf-8");
574614
+ return (0, import_node_fs42.readFileSync)(fullPath, "utf-8");
574369
574615
  } catch {
574370
574616
  return null;
574371
574617
  }
@@ -574421,25 +574667,25 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574421
574667
  };
574422
574668
  }
574423
574669
  movePatternsToReplayYml(patterns) {
574424
- const replayYmlPath = (0, import_node_path92.join)(this.outputDir, ".fern", "replay.yml");
574670
+ const replayYmlPath = (0, import_node_path102.join)(this.outputDir, ".fern", "replay.yml");
574425
574671
  let config5 = {};
574426
- if ((0, import_node_fs36.existsSync)(replayYmlPath)) {
574427
- const content5 = (0, import_node_fs36.readFileSync)(replayYmlPath, "utf-8");
574672
+ if ((0, import_node_fs42.existsSync)(replayYmlPath)) {
574673
+ const content5 = (0, import_node_fs42.readFileSync)(replayYmlPath, "utf-8");
574428
574674
  config5 = (0, import_yaml22.parse)(content5) ?? {};
574429
574675
  }
574430
574676
  const existing = config5.exclude ?? [];
574431
574677
  const merged = [.../* @__PURE__ */ new Set([...existing, ...patterns])];
574432
574678
  config5.exclude = merged;
574433
- const dir = (0, import_node_path92.dirname)(replayYmlPath);
574434
- if (!(0, import_node_fs36.existsSync)(dir)) {
574435
- (0, import_node_fs36.mkdirSync)(dir, { recursive: true });
574679
+ const dir = (0, import_node_path102.dirname)(replayYmlPath);
574680
+ if (!(0, import_node_fs42.existsSync)(dir)) {
574681
+ (0, import_node_fs42.mkdirSync)(dir, { recursive: true });
574436
574682
  }
574437
- (0, import_node_fs36.writeFileSync)(replayYmlPath, (0, import_yaml22.stringify)(config5, { lineWidth: 0 }), "utf-8");
574683
+ (0, import_node_fs42.writeFileSync)(replayYmlPath, (0, import_yaml22.stringify)(config5, { lineWidth: 0 }), "utf-8");
574438
574684
  }
574439
574685
  };
574440
574686
  var import_node_crypto32 = require("crypto");
574441
- var import_node_fs42 = require("fs");
574442
- var import_node_path102 = require("path");
574687
+ var import_node_fs52 = require("fs");
574688
+ var import_node_path112 = require("path");
574443
574689
  init_GitClient();
574444
574690
  async function bootstrap(outputDir, options2) {
574445
574691
  const git = new GitClient(outputDir);
@@ -574606,10 +574852,10 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574606
574852
  }
574607
574853
  var REPLAY_FERNIGNORE_ENTRIES = [".fern/replay.lock", ".fern/replay.yml", ".gitattributes"];
574608
574854
  function ensureFernignoreEntries(outputDir) {
574609
- const fernignorePath = (0, import_node_path102.join)(outputDir, ".fernignore");
574855
+ const fernignorePath = (0, import_node_path112.join)(outputDir, ".fernignore");
574610
574856
  let content5 = "";
574611
- if ((0, import_node_fs42.existsSync)(fernignorePath)) {
574612
- content5 = (0, import_node_fs42.readFileSync)(fernignorePath, "utf-8");
574857
+ if ((0, import_node_fs52.existsSync)(fernignorePath)) {
574858
+ content5 = (0, import_node_fs52.readFileSync)(fernignorePath, "utf-8");
574613
574859
  }
574614
574860
  const lines = content5.split("\n");
574615
574861
  const toAdd = [];
@@ -574625,15 +574871,15 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574625
574871
  content5 += "\n";
574626
574872
  }
574627
574873
  content5 += toAdd.join("\n") + "\n";
574628
- (0, import_node_fs42.writeFileSync)(fernignorePath, content5, "utf-8");
574874
+ (0, import_node_fs52.writeFileSync)(fernignorePath, content5, "utf-8");
574629
574875
  return true;
574630
574876
  }
574631
574877
  var GITATTRIBUTES_ENTRIES = [".fern/replay.lock linguist-generated=true"];
574632
574878
  function ensureGitattributesEntries(outputDir) {
574633
- const gitattributesPath = (0, import_node_path102.join)(outputDir, ".gitattributes");
574879
+ const gitattributesPath = (0, import_node_path112.join)(outputDir, ".gitattributes");
574634
574880
  let content5 = "";
574635
- if ((0, import_node_fs42.existsSync)(gitattributesPath)) {
574636
- content5 = (0, import_node_fs42.readFileSync)(gitattributesPath, "utf-8");
574881
+ if ((0, import_node_fs52.existsSync)(gitattributesPath)) {
574882
+ content5 = (0, import_node_fs52.readFileSync)(gitattributesPath, "utf-8");
574637
574883
  }
574638
574884
  const lines = content5.split("\n");
574639
574885
  const toAdd = [];
@@ -574649,7 +574895,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574649
574895
  content5 += "\n";
574650
574896
  }
574651
574897
  content5 += toAdd.join("\n") + "\n";
574652
- (0, import_node_fs42.writeFileSync)(gitattributesPath, content5, "utf-8");
574898
+ (0, import_node_fs52.writeFileSync)(gitattributesPath, content5, "utf-8");
574653
574899
  }
574654
574900
  function computeContentHash(patchContent) {
574655
574901
  const lines = patchContent.split("\n");
@@ -574659,6 +574905,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574659
574905
  return `sha256:${(0, import_node_crypto32.createHash)("sha256").update(normalized).digest("hex")}`;
574660
574906
  }
574661
574907
  var import_minimatch72 = require_commonjs3();
574908
+ init_GitClient();
574662
574909
  function parseDiffStat(patchContent) {
574663
574910
  let additions = 0;
574664
574911
  let deletions = 0;
@@ -574717,7 +574964,28 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574717
574964
  totalPatches: 0,
574718
574965
  warnings: []
574719
574966
  };
574720
- function forget(outputDir, options2) {
574967
+ async function dismissPatches(outputDir, lockManager, patches) {
574968
+ const git = new GitClient(outputDir);
574969
+ const detector = new ReplayDetector(git, lockManager, outputDir, readFernignorePatterns(outputDir));
574970
+ for (const patch5 of patches) {
574971
+ const hashes = [patch5.content_hash];
574972
+ if (patch5.original_commit) {
574973
+ const materialized = await detector.materializeCommitPatch(patch5.original_commit);
574974
+ if (materialized.status === "ok" && !hashes.includes(materialized.contentHash)) {
574975
+ hashes.push(materialized.contentHash);
574976
+ }
574977
+ }
574978
+ lockManager.addDismissal({
574979
+ patch_id: patch5.id,
574980
+ original_commit: patch5.original_commit,
574981
+ original_message: patch5.original_message,
574982
+ content_hashes: hashes,
574983
+ dismissed_at: (/* @__PURE__ */ new Date()).toISOString(),
574984
+ via: "forget"
574985
+ });
574986
+ }
574987
+ }
574988
+ async function forget(outputDir, options2) {
574721
574989
  const lockManager = new LockfileManager(outputDir);
574722
574990
  if (!lockManager.exists()) {
574723
574991
  return { ...EMPTY_RESULT };
@@ -574728,9 +574996,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574728
574996
  const removed = lock.patches.map(toMatchedPatch);
574729
574997
  const warnings = buildWarnings(lock.patches);
574730
574998
  if (!options2.dryRun) {
574731
- for (const patch5 of lock.patches) {
574732
- lockManager.addForgottenHash(patch5.content_hash);
574733
- }
574999
+ await dismissPatches(outputDir, lockManager, lock.patches);
574734
575000
  lockManager.clearPatches();
574735
575001
  lockManager.save();
574736
575002
  }
@@ -574759,8 +575025,8 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574759
575025
  }
574760
575026
  const warnings = buildWarnings(patchesToRemove);
574761
575027
  if (!options2.dryRun) {
575028
+ await dismissPatches(outputDir, lockManager, patchesToRemove);
574762
575029
  for (const patch5 of patchesToRemove) {
574763
- lockManager.addForgottenHash(patch5.content_hash);
574764
575030
  lockManager.removePatch(patch5.id);
574765
575031
  }
574766
575032
  lockManager.save();
@@ -574799,7 +575065,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574799
575065
  matched: lock.patches.map(toMatchedPatch)
574800
575066
  };
574801
575067
  }
574802
- var import_node_fs52 = require("fs");
575068
+ var import_node_fs62 = require("fs");
574803
575069
  function reset2(outputDir, options2) {
574804
575070
  const lockManager = new LockfileManager(outputDir);
574805
575071
  if (!lockManager.exists()) {
@@ -574813,7 +575079,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574813
575079
  const lock = lockManager.read();
574814
575080
  const patchCount = lock.patches.length;
574815
575081
  if (!options2?.dryRun) {
574816
- (0, import_node_fs52.unlinkSync)(lockManager.lockfilePath);
575082
+ (0, import_node_fs62.unlinkSync)(lockManager.lockfilePath);
574817
575083
  }
574818
575084
  return {
574819
575085
  success: true,
@@ -574823,7 +575089,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574823
575089
  };
574824
575090
  }
574825
575091
  var import_promises710 = require("fs/promises");
574826
- var import_node_path112 = require("path");
575092
+ var import_node_path122 = require("path");
574827
575093
  init_GitClient();
574828
575094
  async function resolve15(outputDir, options2) {
574829
575095
  const lockManager = new LockfileManager(outputDir);
@@ -574864,15 +575130,16 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574864
575130
  const patchesToCommit = [...resolvingPatches, ...unresolvedPatches];
574865
575131
  if (patchesToCommit.length > 0) {
574866
575132
  const currentGen = lock.current_generation;
574867
- const detector = new ReplayDetector(git, lockManager, outputDir);
575133
+ const detector = new ReplayDetector(git, lockManager, outputDir, readFernignorePatterns(outputDir));
574868
575134
  const warnings = [];
575135
+ const patchesDismissed = [];
574869
575136
  let patchesResolved = 0;
574870
575137
  const pass1 = [];
574871
575138
  for (const patch5 of patchesToCommit) {
574872
575139
  const result = await computePerPatchDiffWithFallback(
574873
575140
  patch5,
574874
575141
  (f3) => git.showFile(currentGen, f3),
574875
- (f3) => (0, import_promises710.readFile)((0, import_node_path112.join)(outputDir, f3), "utf-8").catch(() => null),
575142
+ (f3) => (0, import_promises710.readFile)((0, import_node_path122.join)(outputDir, f3), "utf-8").catch(() => null),
574876
575143
  (files) => git.exec(["diff", currentGen, "--", ...files]).catch(() => null)
574877
575144
  );
574878
575145
  if (result === null) {
@@ -574906,7 +575173,27 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574906
575173
  const r4 = pass1[i5];
574907
575174
  if (r4.kind === "skip") continue;
574908
575175
  if (r4.kind === "revert") {
575176
+ const hashes = [patch5.content_hash];
575177
+ if (patch5.original_commit) {
575178
+ const materialized = await detector.materializeCommitPatch(patch5.original_commit);
575179
+ if (materialized.status === "ok" && !hashes.includes(materialized.contentHash)) {
575180
+ hashes.push(materialized.contentHash);
575181
+ } else if (materialized.status === "unreachable") {
575182
+ warnings.push(
575183
+ `Patch ${patch5.id}: original commit ${patch5.original_commit.slice(0, 7)} is unreachable; dismissed using the stored content hash only. If this commit reappears in history, the customization may be re-detected once.`
575184
+ );
575185
+ }
575186
+ }
575187
+ lockManager.addDismissal({
575188
+ patch_id: patch5.id,
575189
+ original_commit: patch5.original_commit,
575190
+ original_message: patch5.original_message,
575191
+ content_hashes: hashes,
575192
+ dismissed_at: (/* @__PURE__ */ new Date()).toISOString(),
575193
+ via: "resolve"
575194
+ });
574909
575195
  lockManager.removePatch(patch5.id);
575196
+ patchesDismissed.push({ id: patch5.id, message: patch5.original_message });
574910
575197
  continue;
574911
575198
  }
574912
575199
  if (lastIndexByHash.get(r4.hash) !== i5) {
@@ -574928,7 +575215,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574928
575215
  const theirsSnapshot = {};
574929
575216
  for (const file4 of filesForSnapshot) {
574930
575217
  if (isBinaryFile(file4)) continue;
574931
- const content5 = await (0, import_promises710.readFile)((0, import_node_path112.join)(outputDir, file4), "utf-8").catch(() => null);
575218
+ const content5 = await (0, import_promises710.readFile)((0, import_node_path122.join)(outputDir, file4), "utf-8").catch(() => null);
574932
575219
  if (content5 != null) {
574933
575220
  theirsSnapshot[file4] = content5;
574934
575221
  }
@@ -574955,6 +575242,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
574955
575242
  commitSha: commitSha2,
574956
575243
  phase: "committed",
574957
575244
  patchesResolved,
575245
+ patchesDismissed: patchesDismissed.length > 0 ? patchesDismissed : void 0,
574958
575246
  warnings: warnings.length > 0 ? warnings : void 0
574959
575247
  };
574960
575248
  }
@@ -575000,7 +575288,9 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
575000
575288
  lastGeneration: void 0,
575001
575289
  patches: [],
575002
575290
  unresolvedCount: 0,
575003
- excludePatterns: []
575291
+ excludePatterns: [],
575292
+ dismissed: [],
575293
+ legacyDismissedCount: 0
575004
575294
  };
575005
575295
  }
575006
575296
  const lock = lockManager.read();
@@ -575029,13 +575319,24 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
575029
575319
  }
575030
575320
  const config5 = lockManager.getCustomizationsConfig();
575031
575321
  const excludePatterns = config5.exclude ?? [];
575322
+ const dismissals = lock.dismissals ?? [];
575323
+ const dismissed = dismissals.map((d2) => ({
575324
+ message: d2.original_message,
575325
+ sha: d2.original_commit.slice(0, 7),
575326
+ via: d2.via,
575327
+ dismissedAt: d2.dismissed_at
575328
+ }));
575329
+ const coveredHashes = new Set(dismissals.flatMap((d2) => d2.content_hashes));
575330
+ const legacyDismissedCount = (lock.forgotten_hashes ?? []).filter((h4) => !coveredHashes.has(h4)).length;
575032
575331
  return {
575033
575332
  initialized: true,
575034
575333
  generationCount: lock.generations.length,
575035
575334
  lastGeneration,
575036
575335
  patches,
575037
575336
  unresolvedCount,
575038
- excludePatterns
575337
+ excludePatterns,
575338
+ dismissed,
575339
+ legacyDismissedCount
575039
575340
  };
575040
575341
  }
575041
575342
  }
@@ -608312,7 +608613,7 @@ var require_replay_forget = __commonJS({
608312
608613
  Object.defineProperty(exports2, "__esModule", { value: true });
608313
608614
  exports2.replayForget = replayForget3;
608314
608615
  var replay_1 = require_dist24();
608315
- function replayForget3(params2) {
608616
+ async function replayForget3(params2) {
608316
608617
  return (0, replay_1.forget)(params2.outputDir, params2.options);
608317
608618
  }
608318
608619
  }
@@ -686381,7 +686682,7 @@ var AccessTokenPosthogManager = class {
686381
686682
  properties: {
686382
686683
  ...event,
686383
686684
  ...event.properties,
686384
- version: "5.51.1",
686685
+ version: "5.52.0",
686385
686686
  usingAccessToken: true,
686386
686687
  ...getRunIdProperties()
686387
686688
  }
@@ -686447,7 +686748,7 @@ var UserPosthogManager = class {
686447
686748
  distinctId: this.userId ?? await this.getPersistedDistinctId(),
686448
686749
  event: "CLI",
686449
686750
  properties: {
686450
- version: "5.51.1",
686751
+ version: "5.52.0",
686451
686752
  ...event,
686452
686753
  ...event.properties,
686453
686754
  usingAccessToken: false,
@@ -867509,7 +867810,7 @@ var LOCAL_STORAGE_FOLDER4 = ".fern-dev";
867509
867810
  var LOGS_FOLDER_NAME = "logs";
867510
867811
  var MAX_LOGS_DIR_SIZE_BYTES = 100 * 1024 * 1024;
867511
867812
  function getCliSource() {
867512
- const version7 = "5.51.1";
867813
+ const version7 = "5.52.0";
867513
867814
  return `cli@${version7}`;
867514
867815
  }
867515
867816
  var DebugLogger = class {
@@ -895238,6 +895539,13 @@ og:description, headline, excerpt).`
895238
895539
  "static"
895239
895540
  ]
895240
895541
  },
895542
+ "docs.ChangelogLayout": {
895543
+ type: "string",
895544
+ enum: [
895545
+ "timeline",
895546
+ "classic"
895547
+ ]
895548
+ },
895241
895549
  "docs.LayoutConfig": {
895242
895550
  type: "object",
895243
895551
  properties: {
@@ -895383,6 +895691,17 @@ og:description, headline, excerpt).`
895383
895691
  }
895384
895692
  ],
895385
895693
  description: "If `mobile-toc` is set to true, a sticky collapsible table of contents bar will be shown on mobile viewports for guide and overview layout pages."
895694
+ },
895695
+ "changelog-layout": {
895696
+ oneOf: [
895697
+ {
895698
+ $ref: "#/definitions/docs.ChangelogLayout"
895699
+ },
895700
+ {
895701
+ type: "null"
895702
+ }
895703
+ ],
895704
+ description: "Sets which layout to use for changelog pages.\n\n@default: `timeline`\n\n- `timeline` renders the searchable timeline-of-cards layout.\n- `classic` renders the legacy stacked-entries layout with a per-page table of contents."
895386
895705
  }
895387
895706
  },
895388
895707
  additionalProperties: false
@@ -899697,7 +900016,7 @@ var LegacyDocsPublisher = class {
899697
900016
  previewId,
899698
900017
  disableTemplates: void 0,
899699
900018
  skipUpload,
899700
- cliVersion: "5.51.1",
900019
+ cliVersion: "5.52.0",
899701
900020
  loginCommand: "fern auth login"
899702
900021
  });
899703
900022
  if (taskContext.getResult() === TaskResult.Failure) {
@@ -902294,7 +902613,7 @@ var ForgetCommand = class {
902294
902613
  const dryRun = args["dry-run"];
902295
902614
  try {
902296
902615
  if (args.all) {
902297
- const result2 = (0, import_generator_cli.replayForget)({ outputDir, options: { all: true, dryRun } });
902616
+ const result2 = await (0, import_generator_cli.replayForget)({ outputDir, options: { all: true, dryRun } });
902298
902617
  if (!result2.initialized) {
902299
902618
  context3.stderr.info("Replay is not initialized. Nothing to forget.");
902300
902619
  return;
@@ -902317,7 +902636,7 @@ var ForgetCommand = class {
902317
902636
  return;
902318
902637
  }
902319
902638
  if (positionalArgs.length > 0 && positionalArgs.every((a3) => a3.startsWith("patch-"))) {
902320
- const result2 = (0, import_generator_cli.replayForget)({ outputDir, options: { patchIds: positionalArgs, dryRun } });
902639
+ const result2 = await (0, import_generator_cli.replayForget)({ outputDir, options: { patchIds: positionalArgs, dryRun } });
902321
902640
  if (!result2.initialized) {
902322
902641
  context3.stderr.info("Replay is not initialized. Nothing to forget.");
902323
902642
  return;
@@ -902343,7 +902662,7 @@ var ForgetCommand = class {
902343
902662
  });
902344
902663
  }
902345
902664
  const pattern = positionalArgs.length === 1 ? positionalArgs[0] : void 0;
902346
- const result = (0, import_generator_cli.replayForget)({ outputDir, options: { pattern } });
902665
+ const result = await (0, import_generator_cli.replayForget)({ outputDir, options: { pattern } });
902347
902666
  if (!result.initialized) {
902348
902667
  context3.stderr.info("Replay is not initialized. Nothing to forget.");
902349
902668
  return;
@@ -902386,7 +902705,7 @@ Remove ${matched.length} patch(es)? [y/N] `, resolvePrompt);
902386
902705
  }
902387
902706
  }
902388
902707
  const patchIds = matched.map((p6) => p6.id);
902389
- const removeResult = (0, import_generator_cli.replayForget)({ outputDir, options: { patchIds, dryRun: false } });
902708
+ const removeResult = await (0, import_generator_cli.replayForget)({ outputDir, options: { patchIds, dryRun: false } });
902390
902709
  context3.stderr.info(`Removed ${removeResult.removed.length} patch(es). ${removeResult.remaining} remaining.`);
902391
902710
  for (const warning of removeResult.warnings) {
902392
902711
  context3.stderr.warn(warning);
@@ -924461,9 +924780,9 @@ var DynamicTypeMapper2 = class {
924461
924780
  return this.convertNamed({ named });
924462
924781
  }
924463
924782
  case "optional":
924464
- return go_exports.Type.optional(this.convert({ typeReference: args.typeReference.value }));
924783
+ return this.convertOptionalOrNullable(args.typeReference.value);
924465
924784
  case "nullable":
924466
- return go_exports.Type.optional(this.convert({ typeReference: args.typeReference.value }));
924785
+ return this.convertOptionalOrNullable(args.typeReference.value);
924467
924786
  case "primitive":
924468
924787
  return this.convertPrimitive({ primitive: args.typeReference.value });
924469
924788
  case "set":
@@ -924474,6 +924793,56 @@ var DynamicTypeMapper2 = class {
924474
924793
  assertNever(args.typeReference);
924475
924794
  }
924476
924795
  }
924796
+ convertOptionalOrNullable(innerReference) {
924797
+ if (this.isPointerAliasReference(innerReference)) {
924798
+ return this.convert({ typeReference: innerReference });
924799
+ }
924800
+ return go_exports.Type.optional(this.convert({ typeReference: innerReference }));
924801
+ }
924802
+ /**
924803
+ * Returns true if the type reference resolves to a named alias that already
924804
+ * generates as a pointer type in Go. Also handles the collapse case where
924805
+ * optional(nullable(named(alias))) should not produce a double pointer.
924806
+ */
924807
+ isPointerAliasReference(reference4) {
924808
+ if (reference4.type === "named") {
924809
+ return this.isAliasToPointerType(reference4.value);
924810
+ }
924811
+ if (reference4.type === "optional" || reference4.type === "nullable") {
924812
+ const inner2 = reference4.value;
924813
+ if (inner2.type === "named") {
924814
+ return this.isAliasToPointerType(inner2.value);
924815
+ }
924816
+ }
924817
+ return false;
924818
+ }
924819
+ /**
924820
+ * Checks if a named type is an alias that already generates as a pointer in Go
924821
+ * (e.g. a nullable primitive like *time.Time). Traverses alias chains.
924822
+ */
924823
+ isAliasToPointerType(typeId) {
924824
+ const seen = /* @__PURE__ */ new Set();
924825
+ let currentTypeId = typeId;
924826
+ while (true) {
924827
+ if (seen.has(currentTypeId)) {
924828
+ return false;
924829
+ }
924830
+ seen.add(currentTypeId);
924831
+ const namedType = this.context.resolveNamedType({ typeId: currentTypeId });
924832
+ if (namedType == null || namedType.type !== "alias") {
924833
+ return false;
924834
+ }
924835
+ const aliasOf = namedType.typeReference;
924836
+ if (aliasOf.type === "optional" || aliasOf.type === "nullable") {
924837
+ return true;
924838
+ }
924839
+ if (aliasOf.type === "named") {
924840
+ currentTypeId = aliasOf.value;
924841
+ continue;
924842
+ }
924843
+ return false;
924844
+ }
924845
+ }
924477
924846
  convertLiteral({ literal: literal3 }) {
924478
924847
  switch (literal3.type) {
924479
924848
  case "boolean":
@@ -965838,7 +966207,7 @@ function getAutomationContextFromEnv() {
965838
966207
  config_branch: process.env.FERN_CONFIG_BRANCH,
965839
966208
  config_pr_number: process.env.FERN_CONFIG_PR_NUMBER,
965840
966209
  trigger: process.env.GITHUB_EVENT_NAME,
965841
- cli_version: "5.51.1"
966210
+ cli_version: "5.52.0"
965842
966211
  };
965843
966212
  }
965844
966213
  function isAutomationMode() {
@@ -966670,7 +967039,7 @@ var CliContext = class _CliContext {
966670
967039
  if (false) {
966671
967040
  this.logger.error("CLI_VERSION is not defined");
966672
967041
  }
966673
- return "5.51.1";
967042
+ return "5.52.0";
966674
967043
  }
966675
967044
  getCliName() {
966676
967045
  if (false) {
@@ -984840,7 +985209,7 @@ function addReplayForgetCommand(cli, cliContext) {
984840
985209
  const dryRun = argv.dryRun;
984841
985210
  try {
984842
985211
  if (argv.all) {
984843
- const result2 = (0, import_generator_cli5.replayForget)({ outputDir, options: { all: true, dryRun } });
985212
+ const result2 = await (0, import_generator_cli5.replayForget)({ outputDir, options: { all: true, dryRun } });
984844
985213
  if (!result2.initialized) {
984845
985214
  cliContext.logger.info("Replay is not initialized. Nothing to forget.");
984846
985215
  return;
@@ -984863,7 +985232,7 @@ function addReplayForgetCommand(cli, cliContext) {
984863
985232
  return;
984864
985233
  }
984865
985234
  if (args.length > 0 && args.every((a3) => a3.startsWith("patch-"))) {
984866
- const result2 = (0, import_generator_cli5.replayForget)({ outputDir, options: { patchIds: args, dryRun } });
985235
+ const result2 = await (0, import_generator_cli5.replayForget)({ outputDir, options: { patchIds: args, dryRun } });
984867
985236
  if (!result2.initialized) {
984868
985237
  cliContext.logger.info("Replay is not initialized. Nothing to forget.");
984869
985238
  return;
@@ -984883,7 +985252,7 @@ function addReplayForgetCommand(cli, cliContext) {
984883
985252
  return;
984884
985253
  }
984885
985254
  const pattern = args.length === 1 ? args[0] : void 0;
984886
- const result = (0, import_generator_cli5.replayForget)({ outputDir, options: { pattern } });
985255
+ const result = await (0, import_generator_cli5.replayForget)({ outputDir, options: { pattern } });
984887
985256
  if (!result.initialized) {
984888
985257
  cliContext.logger.info("Replay is not initialized. Nothing to forget.");
984889
985258
  return;
@@ -984926,7 +985295,7 @@ Remove ${matched.length} patch(es)? [y/N] `, resolvePrompt);
984926
985295
  }
984927
985296
  }
984928
985297
  const patchIds = matched.map((p6) => p6.id);
984929
- const removeResult = (0, import_generator_cli5.replayForget)({ outputDir, options: { patchIds, dryRun: false } });
985298
+ const removeResult = await (0, import_generator_cli5.replayForget)({ outputDir, options: { patchIds, dryRun: false } });
984930
985299
  cliContext.logger.info(
984931
985300
  `Removed ${removeResult.removed.length} patch(es). ${removeResult.remaining} remaining.`
984932
985301
  );