@fern-api/fern-api-dev 5.45.3 → 5.46.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.
Files changed (2) hide show
  1. package/cli.cjs +646 -193
  2. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -422153,30 +422153,30 @@ var require_AutoVersionStep = __commonJS({
422153
422153
  }
422154
422154
  async execute(context3) {
422155
422155
  const prepared = context3.previousStepResults.generationCommit?.preparedReplay;
422156
- if (!prepared) {
422157
- this.logger.info("AutoVersionStep: no replay preparation available (replay uninitialized or prepare failed); skipping.");
422158
- return { executed: true, success: true };
422159
- }
422160
- if (prepared.flow === "skip-application") {
422161
- this.logger.info("AutoVersionStep: replay flow is skip-application; skipping autoversion.");
422162
- return { executed: true, success: true };
422163
- }
422164
- const language = this.config.language;
422165
- const mappedMagicVersion = (0, index_1.mapMagicVersionForLanguage)(index_1.MAGIC_VERSION, language);
422166
- const service = new index_1.AutoVersioningService({ logger: this.toTaskLogger() });
422167
- if (prepared.previousGenerationSha == null) {
422168
- return await this.handleFirstGeneration({ service, language, mappedMagicVersion });
422156
+ if (prepared) {
422157
+ if (prepared.flow === "skip-application") {
422158
+ this.logger.info("AutoVersionStep: replay flow is skip-application; skipping autoversion.");
422159
+ return { executed: true, success: true };
422160
+ }
422161
+ const language = this.config.language;
422162
+ const mappedMagicVersion = (0, index_1.mapMagicVersionForLanguage)(index_1.MAGIC_VERSION, language);
422163
+ const service = new index_1.AutoVersioningService({ logger: this.toTaskLogger() });
422164
+ if (prepared.previousGenerationSha == null) {
422165
+ return await this.handleFirstGeneration({ service, language, mappedMagicVersion, commit: true });
422166
+ }
422167
+ return await this.handleNormalFlow({
422168
+ prepared,
422169
+ service,
422170
+ language,
422171
+ mappedMagicVersion,
422172
+ previousGenerationSha: prepared.previousGenerationSha
422173
+ });
422169
422174
  }
422170
- return await this.handleNormalFlow({
422171
- prepared,
422172
- service,
422173
- language,
422174
- mappedMagicVersion,
422175
- previousGenerationSha: prepared.previousGenerationSha
422176
- });
422175
+ this.logger.info("AutoVersionStep: running in non-replay mode (git diff HEAD).");
422176
+ return await this.handleNonReplayFlow();
422177
422177
  }
422178
422178
  async handleFirstGeneration(params2) {
422179
- const { service, language, mappedMagicVersion } = params2;
422179
+ const { service, language, mappedMagicVersion, commit } = params2;
422180
422180
  const initialVersion = this.config.baseVersion ?? (mappedMagicVersion.startsWith("v") ? "v0.0.1" : "0.0.1");
422181
422181
  if (!(0, index_1.isValidSemver)(initialVersion)) {
422182
422182
  const errorMessage = `AutoVersionStep: baseVersion ${JSON.stringify(initialVersion)} is not a valid semver string (expected e.g. "1.2.3" or "v1.2.3"). Refusing to run to avoid shell injection into the placeholder-rewrite step.`;
@@ -422193,7 +422193,7 @@ var require_AutoVersionStep = __commonJS({
422193
422193
  await service.addGoMajorVersionSuffix(this.outputDir, initialVersion);
422194
422194
  }
422195
422195
  const commitMessage = this.brandMessage("Initial SDK generation");
422196
- const commitSha = this.commitAutoversion(commitMessage);
422196
+ const commitSha = commit ? this.commitAutoversion(commitMessage) : void 0;
422197
422197
  return {
422198
422198
  executed: true,
422199
422199
  success: true,
@@ -422212,7 +422212,7 @@ var require_AutoVersionStep = __commonJS({
422212
422212
  baseVersion: this.config.baseVersion
422213
422213
  });
422214
422214
  if (previousVersion == null) {
422215
- return await this.handleFirstGeneration({ service, language, mappedMagicVersion });
422215
+ return await this.handleFirstGeneration({ service, language, mappedMagicVersion, commit: true });
422216
422216
  }
422217
422217
  if (rawDiff.trim().length === 0) {
422218
422218
  this.logger.info(`AutoVersionStep: empty diff between generations; rewriting placeholder to ${previousVersion}.`);
@@ -422248,15 +422248,19 @@ var require_AutoVersionStep = __commonJS({
422248
422248
  analysis: { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") }
422249
422249
  });
422250
422250
  }
422251
- const chunks = service.chunkDiff(cleanedDiff, index_1.MAX_AI_DIFF_BYTES);
422252
- const cappedChunks = chunks.slice(0, index_1.MAX_CHUNKS);
422253
- const skippedChunks = chunks.length - cappedChunks.length;
422254
- if (chunks.length > 1) {
422255
- this.logger.info(`AutoVersionStep: split diff into ${chunks.length} chunks` + (skippedChunks > 0 ? ` (capped at ${index_1.MAX_CHUNKS}, skipping ${skippedChunks})` : ""));
422256
- }
422257
422251
  let analysis;
422258
422252
  try {
422259
- analysis = cappedChunks.length <= 1 ? await this.analyzeSingle(cleanedDiff, language, previousVersion) : await this.analyzeChunks(cappedChunks, language, previousVersion);
422253
+ if (this.config.ai == null && this.config.fernToken != null) {
422254
+ analysis = await this.analyzeViaFaiService(cleanedDiff, language, previousVersion);
422255
+ } else {
422256
+ const chunks = service.chunkDiff(cleanedDiff, index_1.MAX_AI_DIFF_BYTES);
422257
+ const cappedChunks = chunks.slice(0, index_1.MAX_CHUNKS);
422258
+ const skippedChunks = chunks.length - cappedChunks.length;
422259
+ if (chunks.length > 1) {
422260
+ this.logger.info(`AutoVersionStep: split diff into ${chunks.length} chunks` + (skippedChunks > 0 ? ` (capped at ${index_1.MAX_CHUNKS}, skipping ${skippedChunks})` : ""));
422261
+ }
422262
+ analysis = cappedChunks.length <= 1 ? await this.analyzeSingle(cleanedDiff, language, previousVersion) : await this.analyzeChunks(cappedChunks, language, previousVersion);
422263
+ }
422260
422264
  } catch (error50) {
422261
422265
  this.logger.warn(`AutoVersionStep: FAI analysis failed (${String(error50)}); falling back to PATCH bump.`);
422262
422266
  analysis = { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") };
@@ -422279,6 +422283,94 @@ var require_AutoVersionStep = __commonJS({
422279
422283
  analysis
422280
422284
  });
422281
422285
  }
422286
+ /**
422287
+ * Non-replay autoversion: diffs HEAD (committed SDK) vs the working tree
422288
+ * (freshly generated files). Does NOT commit — GithubStep will commit all
422289
+ * changes (including the rewritten placeholder) in a single generation commit.
422290
+ */
422291
+ async handleNonReplayFlow() {
422292
+ const language = this.config.language;
422293
+ const mappedMagicVersion = (0, index_1.mapMagicVersionForLanguage)(index_1.MAGIC_VERSION, language);
422294
+ const service = new index_1.AutoVersioningService({ logger: this.toTaskLogger() });
422295
+ const rawDiff = this.gitDiffHead();
422296
+ const previousVersion = await this.resolvePreviousVersionNonReplay({
422297
+ service,
422298
+ rawDiff,
422299
+ mappedMagicVersion,
422300
+ baseVersion: this.config.baseVersion
422301
+ });
422302
+ if (previousVersion == null) {
422303
+ return await this.handleFirstGeneration({ service, language, mappedMagicVersion, commit: false });
422304
+ }
422305
+ if (rawDiff.trim().length === 0) {
422306
+ this.logger.info(`AutoVersionStep: empty diff (non-replay); rewriting placeholder to ${previousVersion}.`);
422307
+ return await this.finalizeNoChangeNonReplay({
422308
+ service,
422309
+ language,
422310
+ mappedMagicVersion,
422311
+ previousVersion,
422312
+ reason: "no diff between generations"
422313
+ });
422314
+ }
422315
+ const cleanedDiff = service.cleanDiffForAI(rawDiff, mappedMagicVersion);
422316
+ const rawBytes = Buffer.byteLength(rawDiff, "utf-8");
422317
+ const cleanedBytes = Buffer.byteLength(cleanedDiff, "utf-8");
422318
+ this.logger.debug(`AutoVersionStep (non-replay): raw=${(0, index_1.formatSizeKB)(rawBytes)}KB (${(0, index_1.countFilesInDiff)(rawDiff)} files), cleaned=${(0, index_1.formatSizeKB)(cleanedBytes)}KB (${(0, index_1.countFilesInDiff)(cleanedDiff)} files)`);
422319
+ if (cleanedDiff.trim().length === 0) {
422320
+ this.logger.info(`AutoVersionStep: cleaned diff empty (non-replay); rewriting placeholder to ${previousVersion}.`);
422321
+ return await this.finalizeNoChangeNonReplay({
422322
+ service,
422323
+ language,
422324
+ mappedMagicVersion,
422325
+ previousVersion,
422326
+ reason: "no semantic changes"
422327
+ });
422328
+ }
422329
+ if (cleanedBytes > index_1.MAX_RAW_DIFF_BYTES) {
422330
+ this.logger.warn(`AutoVersionStep (non-replay): diff too large (${(0, index_1.formatSizeKB)(cleanedBytes)}KB). Falling back to PATCH.`);
422331
+ return await this.finalizeWithBumpNonReplay({
422332
+ service,
422333
+ language,
422334
+ mappedMagicVersion,
422335
+ previousVersion,
422336
+ analysis: { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") }
422337
+ });
422338
+ }
422339
+ let analysis;
422340
+ try {
422341
+ if (this.config.ai == null && this.config.fernToken != null) {
422342
+ analysis = await this.analyzeViaFaiService(cleanedDiff, language, previousVersion);
422343
+ } else {
422344
+ const chunks = service.chunkDiff(cleanedDiff, index_1.MAX_AI_DIFF_BYTES);
422345
+ const cappedChunks = chunks.slice(0, index_1.MAX_CHUNKS);
422346
+ const skippedChunks = chunks.length - cappedChunks.length;
422347
+ if (chunks.length > 1) {
422348
+ this.logger.info(`AutoVersionStep (non-replay): split diff into ${chunks.length} chunks` + (skippedChunks > 0 ? ` (capped at ${index_1.MAX_CHUNKS}, skipping ${skippedChunks})` : ""));
422349
+ }
422350
+ analysis = cappedChunks.length <= 1 ? await this.analyzeSingle(cleanedDiff, language, previousVersion) : await this.analyzeChunks(cappedChunks, language, previousVersion);
422351
+ }
422352
+ } catch (error50) {
422353
+ this.logger.warn(`AutoVersionStep (non-replay): FAI analysis failed (${String(error50)}); falling back to PATCH bump.`);
422354
+ analysis = { versionBump: "PATCH", message: this.brandMessage("SDK regeneration") };
422355
+ }
422356
+ if (analysis == null) {
422357
+ this.logger.info(`AutoVersionStep (non-replay): FAI returned NO_CHANGE; rewriting placeholder to ${previousVersion}.`);
422358
+ return await this.finalizeNoChangeNonReplay({
422359
+ service,
422360
+ language,
422361
+ mappedMagicVersion,
422362
+ previousVersion,
422363
+ reason: "FAI returned NO_CHANGE"
422364
+ });
422365
+ }
422366
+ return await this.finalizeWithBumpNonReplay({
422367
+ service,
422368
+ language,
422369
+ mappedMagicVersion,
422370
+ previousVersion,
422371
+ analysis
422372
+ });
422373
+ }
422282
422374
  /**
422283
422375
  * Terminal path for runs that determine no semver bump is needed. The
422284
422376
  * current working tree still contains `0.0.0-fern-placeholder` from this
@@ -422336,6 +422428,94 @@ var require_AutoVersionStep = __commonJS({
422336
422428
  commitSha
422337
422429
  };
422338
422430
  }
422431
+ /**
422432
+ * Non-replay variant of finalizeNoChange. Rewrites the placeholder to
422433
+ * `previousVersion` but does NOT commit — GithubStep handles the commit.
422434
+ */
422435
+ async finalizeNoChangeNonReplay(params2) {
422436
+ const { service, language, mappedMagicVersion, previousVersion, reason } = params2;
422437
+ if (!(0, index_1.isValidSemver)(previousVersion)) {
422438
+ const errorMessage = `AutoVersionStep: resolved previousVersion ${JSON.stringify(previousVersion)} is not a valid semver string. Refusing to rewrite placeholder to avoid shell injection.`;
422439
+ this.logger.error(errorMessage);
422440
+ return { executed: true, success: false, errorMessage };
422441
+ }
422442
+ await service.replaceMagicVersion(this.outputDir, mappedMagicVersion, previousVersion);
422443
+ if (language === "go") {
422444
+ await service.addGoMajorVersionSuffix(this.outputDir, previousVersion);
422445
+ }
422446
+ const commitMessage = this.brandMessage(`SDK regeneration (no semver change: ${reason})`);
422447
+ return {
422448
+ executed: true,
422449
+ success: true,
422450
+ version: previousVersion,
422451
+ previousVersion,
422452
+ versionBump: "NO_CHANGE",
422453
+ commitMessage
422454
+ };
422455
+ }
422456
+ /**
422457
+ * Non-replay variant of finalizeWithBump. Rewrites the placeholder to
422458
+ * the bumped version but does NOT commit — GithubStep handles the commit.
422459
+ */
422460
+ async finalizeWithBumpNonReplay(params2) {
422461
+ const { service, language, mappedMagicVersion, previousVersion, analysis } = params2;
422462
+ const newVersion = (0, index_1.incrementVersion)(previousVersion, analysis.versionBump);
422463
+ this.logger.info(`AutoVersionStep (non-replay): ${analysis.versionBump} bump: ${previousVersion} \u2192 ${newVersion}`);
422464
+ await service.replaceMagicVersion(this.outputDir, mappedMagicVersion, newVersion);
422465
+ if (language === "go") {
422466
+ await service.addGoMajorVersionSuffix(this.outputDir, newVersion);
422467
+ }
422468
+ if (analysis.changelogEntry && analysis.changelogEntry.trim().length > 0) {
422469
+ await this.prependChangelogEntry({ version: newVersion, entry: analysis.changelogEntry });
422470
+ }
422471
+ return {
422472
+ executed: true,
422473
+ success: true,
422474
+ version: newVersion,
422475
+ commitMessage: analysis.message,
422476
+ changelogEntry: analysis.changelogEntry,
422477
+ previousVersion,
422478
+ versionBump: analysis.versionBump,
422479
+ prDescription: analysis.prDescription,
422480
+ versionBumpReason: analysis.versionBumpReason
422481
+ };
422482
+ }
422483
+ /**
422484
+ * Resolves the previous version for non-replay mode. Reads HEAD:.fern/metadata.json
422485
+ * (not HEAD~1, since no generation commit exists yet) as the primary fallback.
422486
+ */
422487
+ async resolvePreviousVersionNonReplay(params2) {
422488
+ const { service, rawDiff, mappedMagicVersion, baseVersion } = params2;
422489
+ if (baseVersion != null && (0, index_1.isValidSemver)(baseVersion)) {
422490
+ this.logger.debug(`AutoVersionStep (non-replay): previous version from pipeline baseVersion: ${baseVersion}`);
422491
+ return this.normalizeVersionPrefix(baseVersion, mappedMagicVersion);
422492
+ }
422493
+ try {
422494
+ const extracted = service.extractPreviousVersion(rawDiff, mappedMagicVersion);
422495
+ if (extracted != null) {
422496
+ this.logger.debug(`AutoVersionStep (non-replay): previous version from diff: ${extracted}`);
422497
+ return extracted;
422498
+ }
422499
+ } catch (error50) {
422500
+ if (!(error50 instanceof index_1.AutoVersioningException) || !error50.magicVersionAbsent) {
422501
+ throw error50;
422502
+ }
422503
+ this.logger.info("AutoVersionStep (non-replay): magic version not in diff; trying metadata + git tags.");
422504
+ }
422505
+ const metadataVersion = this.readVersionFromMetadataAtHead();
422506
+ if (metadataVersion != null) {
422507
+ return this.normalizeVersionPrefix(metadataVersion, mappedMagicVersion);
422508
+ }
422509
+ try {
422510
+ const tagVersion = await service.getLatestVersionFromGitTags(this.outputDir);
422511
+ if (tagVersion != null) {
422512
+ return this.normalizeVersionPrefix(tagVersion, mappedMagicVersion);
422513
+ }
422514
+ } catch (error50) {
422515
+ this.logger.debug(`AutoVersionStep (non-replay): git-tags fallback failed (${String(error50)}); ignoring.`);
422516
+ }
422517
+ return null;
422518
+ }
422339
422519
  async resolvePreviousVersion(params2) {
422340
422520
  const { service, rawDiff, mappedMagicVersion, baseVersion } = params2;
422341
422521
  if (baseVersion != null && (0, index_1.isValidSemver)(baseVersion)) {
@@ -422385,6 +422565,42 @@ var require_AutoVersionStep = __commonJS({
422385
422565
  const stripped = version7.startsWith("v") ? version7.slice(1) : version7;
422386
422566
  return mappedMagicVersion.startsWith("v") ? `v${stripped}` : stripped;
422387
422567
  }
422568
+ /**
422569
+ * Reads the sdkVersion from the committed (HEAD) .fern/metadata.json.
422570
+ * Used by the non-replay path where no generation commit exists yet,
422571
+ * so HEAD is the unmodified SDK repository.
422572
+ */
422573
+ readVersionFromMetadataAtHead() {
422574
+ try {
422575
+ const output2 = (0, child_process_1.execFileSync)("git", ["show", "HEAD:.fern/metadata.json"], {
422576
+ cwd: this.outputDir,
422577
+ encoding: "utf-8",
422578
+ stdio: "pipe"
422579
+ });
422580
+ const parsed = JSON.parse(output2);
422581
+ return parsed.sdkVersion ?? void 0;
422582
+ } catch (error50) {
422583
+ this.logger.debug(`AutoVersionStep: failed to read HEAD:.fern/metadata.json (${String(error50)})`);
422584
+ return void 0;
422585
+ }
422586
+ }
422587
+ /**
422588
+ * Computes a diff between HEAD (committed SDK) and the working tree (new
422589
+ * generation). Marks untracked files as intent-to-add so they appear in
422590
+ * the diff, matching LocalTaskHandler.generateDiffFile() behavior.
422591
+ */
422592
+ gitDiffHead() {
422593
+ (0, child_process_1.execFileSync)("git", ["add", "-N", "."], {
422594
+ cwd: this.outputDir,
422595
+ stdio: "pipe"
422596
+ });
422597
+ return (0, child_process_1.execFileSync)("git", ["diff", "HEAD", "--", ".", ":(exclude).fern/metadata.json"], {
422598
+ cwd: this.outputDir,
422599
+ encoding: "utf-8",
422600
+ stdio: "pipe",
422601
+ maxBuffer: 256 * 1024 * 1024
422602
+ });
422603
+ }
422388
422604
  gitDiff(from4, to10) {
422389
422605
  return (0, child_process_1.execFileSync)("git", ["diff", from4, to10, "--", ".", ":(exclude).fern/metadata.json"], {
422390
422606
  cwd: this.outputDir,
@@ -422518,6 +422734,47 @@ ${newBlock}${remainder}`;
422518
422734
  };
422519
422735
  }
422520
422736
  }
422737
+ /**
422738
+ * Calls the hosted FAI service (`/sdks/analyze-commit-diff`) with the fern token.
422739
+ * Used when no BAML `ai` config is supplied (remote generation via fiddle). FAI
422740
+ * handles chunking, parallelism, and retries server-side. Returns null on
422741
+ * NO_CHANGE; throws on transport/HTTP errors so the caller's PATCH fallback applies.
422742
+ */
422743
+ async analyzeViaFaiService(cleanedDiff, language, previousVersion) {
422744
+ const baseUrl = this.config.faiBaseUrl ?? "https://fai.buildwithfern.com";
422745
+ const response = await fetch(`${baseUrl}/sdks/analyze-commit-diff`, {
422746
+ method: "POST",
422747
+ headers: {
422748
+ Authorization: `Bearer ${this.config.fernToken}`,
422749
+ "Content-Type": "application/json"
422750
+ },
422751
+ body: JSON.stringify({
422752
+ diff: cleanedDiff,
422753
+ language,
422754
+ previous_version: previousVersion,
422755
+ prior_changelog: this.config.priorChangelog ?? void 0,
422756
+ spec_commit_message: this.config.specCommitMessage ?? void 0
422757
+ })
422758
+ });
422759
+ if (!response.ok) {
422760
+ const body = await response.text().catch(() => "");
422761
+ throw new Error(`FAI analyze-commit-diff failed with status ${response.status}: ${body.slice(0, 500)}`);
422762
+ }
422763
+ const parsed = await response.json();
422764
+ if (!isFaiAnalyzeResponse(parsed)) {
422765
+ throw new Error("FAI analyze-commit-diff returned an unexpected response shape");
422766
+ }
422767
+ if (parsed.version_bump === "NO_CHANGE") {
422768
+ return null;
422769
+ }
422770
+ return {
422771
+ versionBump: parsed.version_bump,
422772
+ message: this.brandMessage(parsed.message),
422773
+ changelogEntry: nonEmpty2(parsed.changelog_entry),
422774
+ prDescription: nonEmpty2(parsed.pr_description),
422775
+ versionBumpReason: nonEmpty2(parsed.version_bump_reason)
422776
+ };
422777
+ }
422521
422778
  /**
422522
422779
  * Dynamically imports @fern-api/cli-ai only when autoversion actually needs to
422523
422780
  * call FAI. The package is ESM-only with extensionless internal imports, so
@@ -422560,6 +422817,20 @@ ${newBlock}${remainder}`;
422560
422817
  }
422561
422818
  };
422562
422819
  exports2.AutoVersionStep = AutoVersionStep;
422820
+ var FAI_VERSION_BUMPS = ["MAJOR", "MINOR", "PATCH", "NO_CHANGE"];
422821
+ function isFaiAnalyzeResponse(value2) {
422822
+ if (typeof value2 !== "object" || value2 == null) {
422823
+ return false;
422824
+ }
422825
+ const candidate = value2;
422826
+ return typeof candidate.message === "string" && typeof candidate.version_bump === "string" && FAI_VERSION_BUMPS.includes(candidate.version_bump) && isStringOrAbsent(candidate.changelog_entry) && isStringOrAbsent(candidate.pr_description) && isStringOrAbsent(candidate.version_bump_reason);
422827
+ }
422828
+ function isStringOrAbsent(value2) {
422829
+ return value2 == null || typeof value2 === "string";
422830
+ }
422831
+ function nonEmpty2(value2) {
422832
+ return value2 != null && value2.trim().length > 0 ? value2 : void 0;
422833
+ }
422563
422834
  }
422564
422835
  });
422565
422836
 
@@ -428061,9 +428332,9 @@ var require_diff3 = __commonJS({
428061
428332
  }
428062
428333
  });
428063
428334
 
428064
- // ../../../node_modules/.pnpm/@fern-api+replay@0.16.2/node_modules/@fern-api/replay/dist/index.cjs
428335
+ // ../../../node_modules/.pnpm/@fern-api+replay@0.18.0/node_modules/@fern-api/replay/dist/index.cjs
428065
428336
  var require_dist24 = __commonJS({
428066
- "../../../node_modules/.pnpm/@fern-api+replay@0.16.2/node_modules/@fern-api/replay/dist/index.cjs"(exports2, module4) {
428337
+ "../../../node_modules/.pnpm/@fern-api+replay@0.18.0/node_modules/@fern-api/replay/dist/index.cjs"(exports2, module4) {
428067
428338
  "use strict";
428068
428339
  var __defProp4 = Object.defineProperty;
428069
428340
  var __getOwnPropDesc3 = Object.getOwnPropertyDescriptor;
@@ -428117,6 +428388,11 @@ var require_dist24 = __commonJS({
428117
428388
  proc2.stderr.on("data", (data2) => {
428118
428389
  stderr += data2.toString();
428119
428390
  });
428391
+ proc2.on("error", (err) => {
428392
+ reject(new Error(`git ${args.join(" ")} failed to spawn: ${err.message}`));
428393
+ });
428394
+ proc2.stdin.on("error", () => {
428395
+ });
428120
428396
  proc2.on("close", (code5) => {
428121
428397
  if (code5 === 0) {
428122
428398
  resolve22(stdout);
@@ -428900,7 +429176,8 @@ var require_dist24 = __commonJS({
428900
429176
  var INFRASTRUCTURE_FILES = /* @__PURE__ */ new Set([".fernignore"]);
428901
429177
  function matchesFernignorePattern(filePath, patterns) {
428902
429178
  if (patterns.length === 0) return false;
428903
- return patterns.some((p14) => {
429179
+ return patterns.some((rawPattern) => {
429180
+ const p14 = rawPattern.endsWith("/") ? rawPattern.slice(0, -1) : rawPattern;
428904
429181
  if (filePath === p14) return true;
428905
429182
  if ((0, import_minimatch22.minimatch)(filePath, p14)) return true;
428906
429183
  if (!p14.includes("*") && !p14.includes("?") && filePath.startsWith(p14 + "/")) return true;
@@ -429000,6 +429277,22 @@ var require_dist24 = __commonJS({
429000
429277
  return commit.sha;
429001
429278
  }
429002
429279
  }
429280
+ let fullLog;
429281
+ try {
429282
+ fullLog = await this.git.exec([
429283
+ "log",
429284
+ "--no-merges",
429285
+ "--format=%H%x00%an%x00%ae%x00%s",
429286
+ "HEAD"
429287
+ ]);
429288
+ } catch {
429289
+ return null;
429290
+ }
429291
+ for (const commit of this.parseGitLog(fullLog)) {
429292
+ if (isGenerationBoundary(commit)) {
429293
+ return commit.sha;
429294
+ }
429295
+ }
429003
429296
  return null;
429004
429297
  }
429005
429298
  async detectNewPatches() {
@@ -429011,6 +429304,11 @@ var require_dist24 = __commonJS({
429011
429304
  }
429012
429305
  const lastGen = this.getLastGeneration(lock);
429013
429306
  if (!lastGen) {
429307
+ if (lock.current_generation) {
429308
+ this.warnings.push(
429309
+ `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.`
429310
+ );
429311
+ }
429014
429312
  return { patches: [], revertedPatchIds: [] };
429015
429313
  }
429016
429314
  const exists2 = await this.git.commitExists(lastGen.commit_sha);
@@ -429058,20 +429356,15 @@ var require_dist24 = __commonJS({
429058
429356
  continue;
429059
429357
  }
429060
429358
  const parents = await this.git.getCommitParents(commit.sha);
429061
- let patchContent;
429359
+ let filesOutput;
429062
429360
  try {
429063
- patchContent = await this.git.formatPatch(commit.sha);
429361
+ filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
429064
429362
  } catch {
429065
429363
  this.warnings.push(
429066
429364
  `Could not generate patch for commit ${commit.sha.slice(0, 7)} \u2014 it may be unreachable in a shallow clone. Skipping.`
429067
429365
  );
429068
429366
  continue;
429069
429367
  }
429070
- let contentHash = this.computeContentHash(patchContent);
429071
- if (lock.patches.find((p14) => p14.content_hash === contentHash) || forgottenHashes.has(contentHash)) {
429072
- continue;
429073
- }
429074
- const filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
429075
429368
  const { files, sensitiveFiles, fernignoredFiles } = filterInfrastructureAndSensitiveFiles(
429076
429369
  filesOutput,
429077
429370
  this.fernignorePatterns
@@ -429086,19 +429379,31 @@ var require_dist24 = __commonJS({
429086
429379
  `.fernignore-protected file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${fernignoredFiles.join(", ")}. These files are managed by .fernignore, not Replay.`
429087
429380
  );
429088
429381
  }
429089
- if ((sensitiveFiles.length > 0 || fernignoredFiles.length > 0) && files.length > 0) {
429090
- const parentSha = parents[0];
429091
- if (parentSha) {
429092
- try {
429093
- patchContent = await this.git.exec(["diff", parentSha, commit.sha, "--", ...files]);
429094
- } catch {
429095
- continue;
429096
- }
429097
- if (!patchContent.trim()) continue;
429098
- contentHash = this.computeContentHash(patchContent);
429382
+ if (files.length === 0) {
429383
+ continue;
429384
+ }
429385
+ const wasFiltered = sensitiveFiles.length > 0 || fernignoredFiles.length > 0;
429386
+ const parentSha = parents[0];
429387
+ let patchContent;
429388
+ if (wasFiltered && parentSha) {
429389
+ try {
429390
+ patchContent = await this.git.exec(["diff", parentSha, commit.sha, "--", ...files]);
429391
+ } catch {
429392
+ continue;
429393
+ }
429394
+ if (!patchContent.trim()) continue;
429395
+ } else {
429396
+ try {
429397
+ patchContent = await this.git.formatPatch(commit.sha);
429398
+ } catch {
429399
+ this.warnings.push(
429400
+ `Could not generate patch for commit ${commit.sha.slice(0, 7)} \u2014 it may be unreachable in a shallow clone. Skipping.`
429401
+ );
429402
+ continue;
429099
429403
  }
429100
429404
  }
429101
- if (files.length === 0) {
429405
+ const contentHash = this.computeContentHash(patchContent);
429406
+ if (lock.patches.find((p14) => p14.content_hash === contentHash) || forgottenHashes.has(contentHash)) {
429102
429407
  continue;
429103
429408
  }
429104
429409
  const theirsSnapshot = await capturePatchSnapshot(this.git, files, commit.sha);
@@ -429757,16 +430062,20 @@ var require_dist24 = __commonJS({
429757
430062
  const oursPath = (0, import_node_path310.join)(outputDir, resolvedPath);
429758
430063
  const ours = await (0, import_promises208.readFile)(oursPath, "utf-8").catch(() => null);
429759
430064
  let ghostTheirs = null;
429760
- if (!base4 && ours && !renameSourcePath) {
430065
+ let baseTreeUnreachable = false;
430066
+ if (!base4 && !renameSourcePath) {
429761
430067
  const treeReachable = await isTreeReachable(baseGen.tree_hash);
429762
430068
  if (!treeReachable) {
429763
- const fileDiff = extractFileDiff2(patch5.patch_content, filePath);
429764
- if (fileDiff) {
429765
- const { reconstructFromGhostPatch: reconstructFromGhostPatch2 } = await Promise.resolve().then(() => (init_HybridReconstruction(), HybridReconstruction_exports));
429766
- const result = reconstructFromGhostPatch2(fileDiff, ours);
429767
- if (result) {
429768
- base4 = result.base;
429769
- ghostTheirs = result.theirs;
430069
+ baseTreeUnreachable = true;
430070
+ if (ours) {
430071
+ const fileDiff = extractFileDiff2(patch5.patch_content, filePath);
430072
+ if (fileDiff) {
430073
+ const { reconstructFromGhostPatch: reconstructFromGhostPatch2 } = await Promise.resolve().then(() => (init_HybridReconstruction(), HybridReconstruction_exports));
430074
+ const result = reconstructFromGhostPatch2(fileDiff, ours);
430075
+ if (result) {
430076
+ base4 = result.base;
430077
+ ghostTheirs = result.theirs;
430078
+ }
429770
430079
  }
429771
430080
  }
429772
430081
  }
@@ -429778,7 +430087,11 @@ var require_dist24 = __commonJS({
429778
430087
  ours,
429779
430088
  oursPath,
429780
430089
  renameSourcePath,
429781
- ghostTheirs
430090
+ ghostTheirs,
430091
+ // Only meaningful when ghost reconstruction did NOT recover a base; if it
430092
+ // did, `base` is non-null and the merge proceeds normally. We still set
430093
+ // the flag truthfully (the tree IS unreachable) so Phase 3 can decide.
430094
+ baseTreeUnreachable
429782
430095
  };
429783
430096
  }
429784
430097
  var CONFLICT_OPENER = "<<<<<<< Generated";
@@ -429957,7 +430270,11 @@ var require_dist24 = __commonJS({
429957
430270
  return { kind: "new-file-only-user", theirs: effective_theirs };
429958
430271
  }
429959
430272
  if (base4 == null && ours != null && effective_theirs != null && !useAccumulatorAsMergeBase) {
429960
- return { kind: "new-file-both", theirs: effective_theirs };
430273
+ return {
430274
+ kind: "new-file-both",
430275
+ theirs: effective_theirs,
430276
+ ...inputs.baseTreeUnreachable ? { baseTreeUnreachable: true } : {}
430277
+ };
429961
430278
  }
429962
430279
  if (effective_theirs == null) {
429963
430280
  return { kind: "skipped", reason: "missing-content" };
@@ -430003,17 +430320,19 @@ var require_dist24 = __commonJS({
430003
430320
  const merged2 = threeWayMerge("", ours, plan.theirs);
430004
430321
  await (0, import_promises210.mkdir)((0, import_node_path44.dirname)(oursPath), { recursive: true });
430005
430322
  await (0, import_promises210.writeFile)(oursPath, merged2.content);
430323
+ const unreachableFlag = plan.baseTreeUnreachable ? { baseTreeUnreachable: true } : {};
430006
430324
  if (merged2.hasConflicts) {
430007
430325
  return {
430008
430326
  file: resolvedPath,
430009
430327
  status: "conflict",
430010
430328
  conflicts: merged2.conflicts,
430011
430329
  conflictReason: "new-file-both",
430012
- conflictMetadata: metadata
430330
+ conflictMetadata: metadata,
430331
+ ...unreachableFlag
430013
430332
  };
430014
430333
  }
430015
430334
  accumulator.recordMerge(resolvedPath, merged2.content, patch5.base_generation);
430016
- return { file: resolvedPath, status: "merged" };
430335
+ return { file: resolvedPath, status: "merged", ...unreachableFlag };
430017
430336
  }
430018
430337
  const merged = threeWayMerge(plan.mergeBase, ours, plan.theirs);
430019
430338
  await (0, import_promises210.mkdir)((0, import_node_path44.dirname)(oursPath), { recursive: true });
@@ -431213,6 +431532,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
431213
431532
  results = await this.service.applicator.applyPatches(newPatches);
431214
431533
  this.service._lastApplyResults = results;
431215
431534
  this.service.recordDroppedContextLineWarnings(results);
431535
+ this.service.recordUnreachableBaseWarnings(results);
431216
431536
  this.service.revertConflictingFiles(results);
431217
431537
  for (const result of results) {
431218
431538
  if (result.status === "conflict") {
@@ -431366,6 +431686,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
431366
431686
  const results = await this.service.applicator.applyPatches(allPatches);
431367
431687
  this.service._lastApplyResults = results;
431368
431688
  this.service.recordDroppedContextLineWarnings(results);
431689
+ this.service.recordUnreachableBaseWarnings(results);
431369
431690
  this.service.revertConflictingFiles(results);
431370
431691
  for (const result of results) {
431371
431692
  if (result.status === "conflict") {
@@ -432283,6 +432604,37 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
432283
432604
  }
432284
432605
  }
432285
432606
  }
432607
+ /**
432608
+ * After applyPatches(), surface a warning for each (patch × file) where a
432609
+ * customization's BASE could not be read because the patch's
432610
+ * base-generation tree is unreachable in this clone (shallow-clone window
432611
+ * too small / pruned history). In that state the merge pipeline falls
432612
+ * through to `new-file-both` and the customer's edit can be dropped with NO
432613
+ * conflict and NO error — the INC-866 silent-loss class. This converts that
432614
+ * silence into an explicit, actionable warning naming the file and the
432615
+ * likely cause (insufficient clone window).
432616
+ *
432617
+ * Mirrors `recordDroppedContextLineWarnings`'s "surface, never silently
432618
+ * drop" contract. De-duplicated per file path so a single insufficient
432619
+ * window doesn't spam one warning per touched file repeatedly.
432620
+ */
432621
+ /** @internal Used by Flow implementations in `src/flows/`. */
432622
+ recordUnreachableBaseWarnings(results) {
432623
+ const warned2 = /* @__PURE__ */ new Set();
432624
+ for (const result of results) {
432625
+ if (!result.fileResults) continue;
432626
+ for (const fr9 of result.fileResults) {
432627
+ if (!fr9.baseTreeUnreachable) continue;
432628
+ if (warned2.has(fr9.file)) continue;
432629
+ warned2.add(fr9.file);
432630
+ const base4 = fr9.conflictMetadata?.baseGeneration;
432631
+ const baseRef = base4 ? ` (base generation ${base4.slice(0, 7)})` : "";
432632
+ this.warnings.push(
432633
+ `${fr9.file}: could not read the prior generation's version of this file${baseRef} because that generation's tree is outside the clone window (insufficient shallow-clone depth or pruned history). The customization was treated as a new file, which can drop your edits silently. Re-run with the prior generation in the clone window \u2014 e.g. \`git fetch --shallow-since=<date just before the prior [fern-generated] commit>\` or \`git fetch --unshallow\` \u2014 then re-run replay.`
432634
+ );
432635
+ }
432636
+ }
432637
+ }
432286
432638
  /**
432287
432639
  * After applyPatches(), strip conflict markers from conflicting files
432288
432640
  * so only clean content is committed. Keeps the Generated (OURS) side.
@@ -438785,10 +439137,6 @@ var require_PostGenerationPipeline = __commonJS({
438785
439137
  this.logger.warn(`Replay is not supported with GitHub ${config5.github.mode} mode. Disabling replay to prevent push to base branch.`);
438786
439138
  replayEnabled = false;
438787
439139
  }
438788
- if (autoVersionEnabled && !replayEnabled) {
438789
- this.logger.warn("AutoVersion requires Replay to be enabled. Disabling AutoVersion for this run.");
438790
- autoVersionEnabled = false;
438791
- }
438792
439140
  if (replayEnabled && config5.replay != null) {
438793
439141
  this.steps.push(new GenerationCommitStep_1.GenerationCommitStep(config5.outputDir, this.logger, { enabled: true, skipApplication: config5.replay.skipApplication }, config5.cliVersion, config5.generatorVersions));
438794
439142
  }
@@ -669211,7 +669559,7 @@ var AccessTokenPosthogManager = class {
669211
669559
  properties: {
669212
669560
  ...event,
669213
669561
  ...event.properties,
669214
- version: "5.45.3",
669562
+ version: "5.46.1",
669215
669563
  usingAccessToken: true,
669216
669564
  ...getRunIdProperties()
669217
669565
  }
@@ -669275,7 +669623,7 @@ var UserPosthogManager = class {
669275
669623
  distinctId: this.userId ?? await this.getPersistedDistinctId(),
669276
669624
  event: "CLI",
669277
669625
  properties: {
669278
- version: "5.45.3",
669626
+ version: "5.46.1",
669279
669627
  ...event,
669280
669628
  ...event.properties,
669281
669629
  usingAccessToken: false,
@@ -856646,7 +856994,7 @@ function createDocsConfigFileAstVisitorForRules({ relativeFilepath, allRulesWith
856646
856994
  const ruleViolations = await visitFromRule(node4);
856647
856995
  const severityOverride = severityOverrides?.get(ruleName);
856648
856996
  addViolations(ruleViolations.map((violation) => ({
856649
- name: violation.name,
856997
+ name: violation.name ?? ruleName,
856650
856998
  severity: severityOverride ?? violation.severity,
856651
856999
  relativeFilepath: violation.relativeFilepath ?? RelativeFilePath2.of(""),
856652
857000
  nodePath: violation.nodePath ?? nodePath,
@@ -861013,7 +861361,7 @@ var LOCAL_STORAGE_FOLDER4 = ".fern-dev";
861013
861361
  var LOGS_FOLDER_NAME = "logs";
861014
861362
  var MAX_LOGS_DIR_SIZE_BYTES = 100 * 1024 * 1024;
861015
861363
  function getCliSource() {
861016
- const version7 = "5.45.3";
861364
+ const version7 = "5.46.1";
861017
861365
  return `cli@${version7}`;
861018
861366
  }
861019
861367
  var DebugLogger = class {
@@ -892105,7 +892453,7 @@ var LegacyDocsPublisher = class {
892105
892453
  previewId,
892106
892454
  disableTemplates: void 0,
892107
892455
  skipUpload,
892108
- cliVersion: "5.45.3",
892456
+ cliVersion: "5.46.1",
892109
892457
  loginCommand: "fern auth login"
892110
892458
  });
892111
892459
  if (taskContext.getResult() === TaskResult.Failure) {
@@ -916319,6 +916667,11 @@ var DynamicTypeInstantiationMapper = class {
916319
916667
  writer2.writeNode(collectionLiteral);
916320
916668
  }));
916321
916669
  }
916670
+ // Materializes a literal value (e.g. a literal path parameter), as opposed to
916671
+ // convert(), which treats literals as a no-op since they are usually omitted.
916672
+ convertLiteral(literal3) {
916673
+ return this.convertLiteralValue(literal3);
916674
+ }
916322
916675
  convertLiteralValue(literal3) {
916323
916676
  switch (literal3.type) {
916324
916677
  case "boolean":
@@ -917533,13 +917886,16 @@ var EndpointSnippetGenerator3 = class {
917533
917886
  }
917534
917887
  getBytesBodyRequestArg({ value: value2 }) {
917535
917888
  const bytesValue = typeof value2 === "string" ? value2 : "";
917536
- return go_exports.TypeInstantiation.reference(go_exports.invokeFunc({
917537
- func: go_exports.typeReference({
917538
- name: "NewReader",
917539
- importPath: "bytes"
917540
- }),
917541
- arguments_: [go_exports.TypeInstantiation.bytes(bytesValue)]
917542
- }));
917889
+ if (this.context.customConfig?.useReaderForBytesRequest ?? true) {
917890
+ return go_exports.TypeInstantiation.reference(go_exports.invokeFunc({
917891
+ func: go_exports.typeReference({
917892
+ name: "NewReader",
917893
+ importPath: "bytes"
917894
+ }),
917895
+ arguments_: [go_exports.TypeInstantiation.bytes(bytesValue)]
917896
+ }));
917897
+ }
917898
+ return go_exports.TypeInstantiation.bytes(bytesValue);
917543
917899
  }
917544
917900
  getMethodArgsForInlinedRequest({ request: request7, snippet: snippet2 }) {
917545
917901
  const otherArgs = [];
@@ -917659,7 +918015,7 @@ var EndpointSnippetGenerator3 = class {
917659
918015
  getReferencedRequestBodyPropertyTypeInstantiation({ body, value: value2 }) {
917660
918016
  switch (body.type) {
917661
918017
  case "bytes":
917662
- return this.getBytesBodyRequestArg({ value: value2 });
918018
+ return go_exports.TypeInstantiation.bytes(typeof value2 === "string" ? value2 : "");
917663
918019
  case "typeReference":
917664
918020
  return this.context.dynamicTypeInstantiationMapper.convert({ typeReference: body.value, value: value2 });
917665
918021
  default:
@@ -917687,9 +918043,10 @@ var EndpointSnippetGenerator3 = class {
917687
918043
  values: snippet2.pathParameters ?? {}
917688
918044
  });
917689
918045
  for (const parameter6 of pathParameters) {
918046
+ const value2 = parameter6.typeReference.type === "literal" ? this.context.dynamicTypeInstantiationMapper.convertLiteral(parameter6.typeReference.value) : asPointer ? this.context.dynamicTypeInstantiationMapper.convertToPointerIfPossible(parameter6) : this.context.dynamicTypeInstantiationMapper.convert(parameter6);
917690
918047
  args.push({
917691
918048
  name: this.context.getTypeName(parameter6.name.name),
917692
- value: asPointer ? this.context.dynamicTypeInstantiationMapper.convertToPointerIfPossible(parameter6) : this.context.dynamicTypeInstantiationMapper.convert(parameter6)
918049
+ value: value2
917693
918050
  });
917694
918051
  }
917695
918052
  return args;
@@ -943539,13 +943896,19 @@ function getGithubPublishConfig(githubPublishInfo) {
943539
943896
  secretKeyEnvironmentVariable: (0, import_api50.EnvironmentVariable)(value2.signature.secretKey ?? "")
943540
943897
  } : void 0
943541
943898
  }),
943542
- pypi: (value2) => import_generator_exec_sdk.FernGeneratorExec.GithubPublishInfo.pypi({
943543
- registryUrl: value2.registryUrl,
943544
- packageName: value2.packageName,
943545
- usernameEnvironmentVariable: (0, import_api50.EnvironmentVariable)("PYPI_USERNAME"),
943546
- passwordEnvironmentVariable: (0, import_api50.EnvironmentVariable)("PYPI_PASSWORD"),
943547
- pypiMetadata: value2.pypiMetadata
943548
- }),
943899
+ pypi: (value2) => {
943900
+ const password = (value2.credentials?.password ?? "").trim();
943901
+ const useOidc = password === "<USE_OIDC>" || password === "OIDC";
943902
+ const hasCredentials = value2.credentials != null;
943903
+ return import_generator_exec_sdk.FernGeneratorExec.GithubPublishInfo.pypi({
943904
+ registryUrl: value2.registryUrl,
943905
+ packageName: value2.packageName,
943906
+ usernameEnvironmentVariable: (0, import_api50.EnvironmentVariable)("PYPI_USERNAME"),
943907
+ passwordEnvironmentVariable: (0, import_api50.EnvironmentVariable)(useOidc ? "OIDC" : "PYPI_PASSWORD"),
943908
+ shouldGeneratePublishWorkflow: useOidc || hasCredentials,
943909
+ pypiMetadata: value2.pypiMetadata
943910
+ });
943911
+ },
943549
943912
  rubygems: (value2) => import_generator_exec_sdk.FernGeneratorExec.GithubPublishInfo.rubygems({
943550
943913
  registryUrl: value2.registryUrl,
943551
943914
  packageName: value2.packageName,
@@ -943667,10 +944030,11 @@ function getGeneratorConfig({ generatorInvocation, customConfig, workspaceName,
943667
944030
  });
943668
944031
  }
943669
944032
  });
944033
+ const publishConfig = getPublishConfigForGithubOidc(generatorInvocation, outputVersion);
943670
944034
  return {
943671
944035
  irFilepath: irPath,
943672
944036
  output: output2,
943673
- publish: void 0,
944037
+ publish: publishConfig,
943674
944038
  customConfig,
943675
944039
  workspaceName,
943676
944040
  organization,
@@ -943861,6 +944225,42 @@ function getPublishTargetFromPublishModeV2(mode) {
943861
944225
  _other: () => void 0
943862
944226
  });
943863
944227
  }
944228
+ function getPublishConfigForGithubOidc(generatorInvocation, version7) {
944229
+ const publishInfo = generatorInvocation.outputMode._visit({
944230
+ publish: () => void 0,
944231
+ publishV2: () => void 0,
944232
+ downloadFiles: () => void 0,
944233
+ github: (value2) => value2.publishInfo,
944234
+ githubV2: (value2) => value2._visit({
944235
+ push: (v9) => v9.publishInfo,
944236
+ commitAndRelease: (v9) => v9.publishInfo,
944237
+ pullRequest: (v9) => v9.publishInfo,
944238
+ _other: () => void 0
944239
+ }),
944240
+ _other: () => void 0
944241
+ });
944242
+ if (publishInfo == null || publishInfo.type !== "pypi") {
944243
+ return void 0;
944244
+ }
944245
+ const password = (publishInfo.credentials?.password ?? "").trim();
944246
+ if (password !== "OIDC" && password !== "<USE_OIDC>") {
944247
+ return void 0;
944248
+ }
944249
+ const registriesV2 = structuredClone(emptyRegistriesConfigV2);
944250
+ registriesV2.pypi = {
944251
+ registryUrl: publishInfo.registryUrl,
944252
+ username: "__token__",
944253
+ password: "OIDC",
944254
+ packageName: publishInfo.packageName,
944255
+ pypiMetadata: publishInfo.pypiMetadata
944256
+ };
944257
+ return {
944258
+ registries: structuredClone(emptyRegistriesConfig),
944259
+ registriesV2,
944260
+ publishTarget: void 0,
944261
+ version: version7
944262
+ };
944263
+ }
943864
944264
  var emptyRegistriesConfig = {
943865
944265
  maven: { registryUrl: "", username: "", password: "", group: "", signature: void 0 },
943866
944266
  npm: { registryUrl: "", token: "", scope: "" }
@@ -944444,7 +944844,7 @@ var LocalTaskHandler = class {
944444
944844
  const absolutePathToFernignore = AbsoluteFilePath2.of(join8(this.absolutePathToLocalOutput, RelativeFilePath2.of(FERNIGNORE_FILENAME)));
944445
944845
  const fernIgnorePaths = await getFernIgnorePaths({ absolutePathToFernignore });
944446
944846
  const pathsToPreserve = await this.getPathsToPreserve(fernIgnorePaths);
944447
- await (0, import_promises129.cp)(this.absolutePathToLocalOutput, tmpOutputResolutionDir, { recursive: true });
944847
+ await (0, import_promises129.cp)(this.absolutePathToLocalOutput, tmpOutputResolutionDir, { recursive: true, verbatimSymlinks: true });
944448
944848
  await this.runThrowawayGitCommand(["init"], tmpOutputResolutionDir);
944449
944849
  await this.runThrowawayGitCommand(["config", "gc.auto", "0"], tmpOutputResolutionDir);
944450
944850
  await this.runThrowawayGitCommand(["add", "."], tmpOutputResolutionDir);
@@ -944472,7 +944872,7 @@ var LocalTaskHandler = class {
944472
944872
  retryDelay: 100
944473
944873
  });
944474
944874
  await (0, import_promises129.rm)(this.absolutePathToLocalOutput, { recursive: true });
944475
- await (0, import_promises129.cp)(tmpOutputResolutionDir, this.absolutePathToLocalOutput, { recursive: true });
944875
+ await (0, import_promises129.cp)(tmpOutputResolutionDir, this.absolutePathToLocalOutput, { recursive: true, verbatimSymlinks: true });
944476
944876
  }
944477
944877
  async copyGeneratedFilesNoFernIgnorePreservingGit() {
944478
944878
  const contents = await (0, import_promises129.readdir)(this.absolutePathToLocalOutput);
@@ -944500,10 +944900,10 @@ var LocalTaskHandler = class {
944500
944900
  if (firstLocalOutputItem.endsWith(".zip")) {
944501
944901
  await (0, import_decompress2.default)(join8(this.absolutePathToTmpOutputDirectory, RelativeFilePath2.of(firstLocalOutputItem)), outputPath);
944502
944902
  for (const localOutputItem of remaininglocalOutputItems) {
944503
- await (0, import_promises129.cp)(join8(this.absolutePathToTmpOutputDirectory, RelativeFilePath2.of(localOutputItem)), join8(outputPath, RelativeFilePath2.of(localOutputItem)), { recursive: true });
944903
+ await (0, import_promises129.cp)(join8(this.absolutePathToTmpOutputDirectory, RelativeFilePath2.of(localOutputItem)), join8(outputPath, RelativeFilePath2.of(localOutputItem)), { recursive: true, verbatimSymlinks: true });
944504
944904
  }
944505
944905
  } else {
944506
- await (0, import_promises129.cp)(this.absolutePathToTmpOutputDirectory, outputPath, { recursive: true });
944906
+ await (0, import_promises129.cp)(this.absolutePathToTmpOutputDirectory, outputPath, { recursive: true, verbatimSymlinks: true });
944507
944907
  }
944508
944908
  }
944509
944909
  async copySnippetJSON({ absolutePathToTmpSnippetJSON, absolutePathToLocalSnippetJSON }) {
@@ -957243,7 +957643,7 @@ function getAutomationContextFromEnv() {
957243
957643
  config_branch: process.env.FERN_CONFIG_BRANCH,
957244
957644
  config_pr_number: process.env.FERN_CONFIG_PR_NUMBER,
957245
957645
  trigger: process.env.GITHUB_EVENT_NAME,
957246
- cli_version: "5.45.3"
957646
+ cli_version: "5.46.1"
957247
957647
  };
957248
957648
  }
957249
957649
  function isAutomationMode() {
@@ -958069,7 +958469,7 @@ var CliContext = class _CliContext {
958069
958469
  if (false) {
958070
958470
  this.logger.error("CLI_VERSION is not defined");
958071
958471
  }
958072
- return "5.45.3";
958472
+ return "5.46.1";
958073
958473
  }
958074
958474
  getCliName() {
958075
958475
  if (false) {
@@ -972785,101 +973185,132 @@ async function validateWorkspaces({
972785
973185
  const apiResults = [];
972786
973186
  let docsResult;
972787
973187
  let hasAnyErrors = false;
973188
+ let abortReason;
972788
973189
  const apiWorkspacesToValidate = commandLineApiWorkspace != null ? project.apiWorkspaces.filter((workspace) => workspace.workspaceName === commandLineApiWorkspace) : project.apiWorkspaces;
972789
- const docsWorkspace = project.docsWorkspaces;
972790
- if (docsWorkspace != null) {
972791
- const excludeRules = brokenLinks || errorOnBrokenLinks ? [] : ["valid-markdown-links"];
972792
- const ossWorkspaces = await filterOssWorkspaces(project);
972793
- let collected;
972794
- await cliContext.runTaskForWorkspace(docsWorkspace, async (context3) => {
972795
- collected = await collectDocsWorkspaceViolations({
972796
- workspace: docsWorkspace,
972797
- context: context3,
972798
- apiWorkspaces: project.apiWorkspaces,
972799
- ossWorkspaces,
972800
- errorOnBrokenLinks,
972801
- excludeRules
972802
- });
972803
- });
972804
- if (collected != null) {
972805
- docsResult = {
972806
- violations: collected.violations,
972807
- elapsedMillis: collected.elapsedMillis
972808
- };
972809
- if (collected.hasErrors) {
972810
- hasAnyErrors = true;
972811
- }
972812
- }
972813
- }
972814
- await Promise.all(
972815
- apiWorkspacesToValidate.map(async (workspace) => {
972816
- if (workspace.generatorsConfiguration?.groups.length === 0 && workspace.type !== "fern") {
972817
- return;
972818
- }
972819
- if (workspace instanceof OSSWorkspace && directFromOpenapi) {
972820
- await cliContext.runTaskForWorkspace(workspace, async (context3) => {
972821
- await workspace.getIntermediateRepresentation({
972822
- context: context3,
972823
- audiences: { type: "all" },
972824
- enableUniqueErrorsPerEndpoint: false,
972825
- generateV1Examples: false,
972826
- logWarnings
972827
- });
972828
- });
972829
- return;
972830
- }
972831
- if (workspace instanceof LazyFernWorkspace) {
972832
- const absolutePathToApiYml = join8(
972833
- workspace.absoluteFilePath,
972834
- RelativeFilePath2.of(DEFINITION_DIRECTORY),
972835
- RelativeFilePath2.of(ROOT_API_FILENAME)
972836
- );
972837
- const apiYmlExists = await doesPathExist(absolutePathToApiYml);
972838
- if (!apiYmlExists) {
972839
- await cliContext.runTask(async (context3) => {
972840
- context3.logger.error(`Missing file: ${ROOT_API_FILENAME}`);
972841
- return context3.failAndThrow(void 0, void 0, { code: CliError.Code.ValidationError });
972842
- });
972843
- return;
972844
- }
972845
- }
973190
+ let hasErrors = false;
973191
+ try {
973192
+ const docsWorkspace = project.docsWorkspaces;
973193
+ if (docsWorkspace != null) {
973194
+ const excludeRules = brokenLinks || errorOnBrokenLinks ? [] : ["valid-markdown-links"];
973195
+ const ossWorkspaces = await filterOssWorkspaces(project);
972846
973196
  let collected;
972847
- await cliContext.runTaskForWorkspace(workspace, async (context3) => {
972848
- const fernWorkspace = await workspace.toFernWorkspace({ context: context3 });
972849
- collected = await collectAPIWorkspaceViolations({
972850
- workspace: fernWorkspace,
973197
+ await cliContext.runTaskForWorkspace(docsWorkspace, async (context3) => {
973198
+ collected = await collectDocsWorkspaceViolations({
973199
+ workspace: docsWorkspace,
972851
973200
  context: context3,
972852
- ossWorkspace: workspace instanceof OSSWorkspace ? workspace : void 0
973201
+ apiWorkspaces: project.apiWorkspaces,
973202
+ ossWorkspaces,
973203
+ errorOnBrokenLinks,
973204
+ excludeRules
972853
973205
  });
972854
973206
  });
972855
973207
  if (collected != null) {
972856
- apiResults.push({
972857
- apiName: collected.apiName,
973208
+ docsResult = {
972858
973209
  violations: collected.violations,
972859
973210
  elapsedMillis: collected.elapsedMillis
972860
- });
973211
+ };
972861
973212
  if (collected.hasErrors) {
972862
973213
  hasAnyErrors = true;
972863
973214
  }
972864
973215
  }
972865
- })
972866
- );
972867
- const { hasErrors } = await cliContext.runTask((context3) => {
972868
- return printCheckReport({
972869
- apiResults,
972870
- docsResult,
972871
- logWarnings,
972872
- context: context3
972873
- });
972874
- });
972875
- if (cliContext.isJsonMode) {
972876
- const showApiNames = apiResults.length > 1;
972877
- cliContext.writeJsonToStdout(
972878
- buildCheckJsonResult({ apiResults, docsResult, hasErrors: hasErrors || hasAnyErrors, showApiNames })
973216
+ }
973217
+ await Promise.all(
973218
+ apiWorkspacesToValidate.map(async (workspace) => {
973219
+ if (workspace.generatorsConfiguration?.groups.length === 0 && workspace.type !== "fern") {
973220
+ return;
973221
+ }
973222
+ if (workspace instanceof OSSWorkspace && directFromOpenapi) {
973223
+ await cliContext.runTaskForWorkspace(workspace, async (context3) => {
973224
+ await workspace.getIntermediateRepresentation({
973225
+ context: context3,
973226
+ audiences: { type: "all" },
973227
+ enableUniqueErrorsPerEndpoint: false,
973228
+ generateV1Examples: false,
973229
+ logWarnings
973230
+ });
973231
+ });
973232
+ return;
973233
+ }
973234
+ if (workspace instanceof LazyFernWorkspace) {
973235
+ const absolutePathToApiYml = join8(
973236
+ workspace.absoluteFilePath,
973237
+ RelativeFilePath2.of(DEFINITION_DIRECTORY),
973238
+ RelativeFilePath2.of(ROOT_API_FILENAME)
973239
+ );
973240
+ const apiYmlExists = await doesPathExist(absolutePathToApiYml);
973241
+ if (!apiYmlExists) {
973242
+ abortReason = "missing api.yml";
973243
+ await cliContext.runTask(async (context3) => {
973244
+ context3.logger.error(`Missing file: ${ROOT_API_FILENAME}`);
973245
+ return context3.failAndThrow(void 0, void 0, {
973246
+ code: CliError.Code.ValidationError
973247
+ });
973248
+ });
973249
+ return;
973250
+ }
973251
+ }
973252
+ let collected;
973253
+ await cliContext.runTaskForWorkspace(workspace, async (context3) => {
973254
+ const fernWorkspace = await workspace.toFernWorkspace({ context: context3 });
973255
+ collected = await collectAPIWorkspaceViolations({
973256
+ workspace: fernWorkspace,
973257
+ context: context3,
973258
+ ossWorkspace: workspace instanceof OSSWorkspace ? workspace : void 0
973259
+ });
973260
+ });
973261
+ if (collected != null) {
973262
+ apiResults.push({
973263
+ apiName: collected.apiName,
973264
+ violations: collected.violations,
973265
+ elapsedMillis: collected.elapsedMillis
973266
+ });
973267
+ if (collected.hasErrors) {
973268
+ hasAnyErrors = true;
973269
+ }
973270
+ }
973271
+ })
972879
973272
  );
972880
- }
972881
- if (hasErrors || hasAnyErrors) {
972882
- cliContext.failAndThrow(void 0, void 0, { code: CliError.Code.ValidationError });
973273
+ const reportResult = await cliContext.runTask((context3) => {
973274
+ return printCheckReport({
973275
+ apiResults,
973276
+ docsResult,
973277
+ logWarnings,
973278
+ context: context3
973279
+ });
973280
+ });
973281
+ hasErrors = reportResult.hasErrors;
973282
+ if (cliContext.isJsonMode) {
973283
+ const showApiNames = apiResults.length > 1;
973284
+ cliContext.writeJsonToStdout(
973285
+ buildCheckJsonResult({ apiResults, docsResult, hasErrors: hasErrors || hasAnyErrors, showApiNames })
973286
+ );
973287
+ }
973288
+ if (hasErrors || hasAnyErrors) {
973289
+ abortReason = "validation errors";
973290
+ cliContext.failAndThrow(void 0, void 0, { code: CliError.Code.ValidationError });
973291
+ }
973292
+ } catch (error50) {
973293
+ if (abortReason == null) {
973294
+ abortReason = error50 instanceof Error ? error50.message.slice(0, 100) : "unexpected error";
973295
+ }
973296
+ throw error50;
973297
+ } finally {
973298
+ const allViolations = [...apiResults.flatMap((r5) => r5.violations), ...docsResult?.violations ?? []];
973299
+ const firedRules = [
973300
+ ...new Set(allViolations.map((v9) => v9.name).filter((name2) => name2 != null))
973301
+ ];
973302
+ const numErrors = allViolations.filter((v9) => v9.severity === "fatal" || v9.severity === "error").length;
973303
+ const numWarnings = allViolations.filter((v9) => v9.severity === "warning").length;
973304
+ cliContext.instrumentPostHogEvent({
973305
+ command: "fern check",
973306
+ properties: {
973307
+ validationRules: firedRules,
973308
+ numErrors,
973309
+ numWarnings,
973310
+ passed: !hasErrors && !hasAnyErrors,
973311
+ abortReason
973312
+ }
973313
+ });
972883
973314
  }
972884
973315
  }
972885
973316
 
@@ -974233,24 +974664,46 @@ function addValidateCommand(cli, cliContext) {
974233
974664
  default: false
974234
974665
  }),
974235
974666
  async (argv) => {
974236
- const project = await loadProjectAndRegisterWorkspacesWithContext(cliContext, {
974237
- commandLineApiWorkspace: void 0,
974238
- defaultToAllApiWorkspaces: true
974239
- });
974240
- if (argv.api != null && !project.apiWorkspaces.some((ws5) => ws5.workspaceName === argv.api)) {
974241
- cliContext.failAndThrow(`API does not exist: ${argv.api}`, void 0, {
974242
- code: CliError.Code.ConfigError
974667
+ let project;
974668
+ try {
974669
+ project = await loadProjectAndRegisterWorkspacesWithContext(cliContext, {
974670
+ commandLineApiWorkspace: void 0,
974671
+ defaultToAllApiWorkspaces: true
974243
974672
  });
974673
+ if (argv.api != null && !project.apiWorkspaces.some((ws5) => ws5.workspaceName === argv.api)) {
974674
+ cliContext.instrumentPostHogEvent({
974675
+ command: "fern check",
974676
+ properties: {
974677
+ passed: false,
974678
+ abortReason: `API does not exist: ${argv.api}`
974679
+ }
974680
+ });
974681
+ cliContext.failAndThrow(`API does not exist: ${argv.api}`, void 0, {
974682
+ code: CliError.Code.ConfigError
974683
+ });
974684
+ }
974685
+ await validateWorkspaces({
974686
+ project,
974687
+ cliContext,
974688
+ logWarnings: argv.warnings,
974689
+ brokenLinks: argv.brokenLinks,
974690
+ errorOnBrokenLinks: argv.strictBrokenLinks,
974691
+ directFromOpenapi: argv.fromOpenapi,
974692
+ commandLineApiWorkspace: argv.api
974693
+ });
974694
+ } catch (error50) {
974695
+ if (project == null) {
974696
+ const reason = error50 instanceof Error ? error50.message.slice(0, 100) : "project load failed";
974697
+ cliContext.instrumentPostHogEvent({
974698
+ command: "fern check",
974699
+ properties: {
974700
+ passed: false,
974701
+ abortReason: reason
974702
+ }
974703
+ });
974704
+ }
974705
+ throw error50;
974244
974706
  }
974245
- await validateWorkspaces({
974246
- project,
974247
- cliContext,
974248
- logWarnings: argv.warnings,
974249
- brokenLinks: argv.brokenLinks,
974250
- errorOnBrokenLinks: argv.strictBrokenLinks,
974251
- directFromOpenapi: argv.fromOpenapi,
974252
- commandLineApiWorkspace: argv.api
974253
- });
974254
974707
  }
974255
974708
  );
974256
974709
  }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.45.3",
2
+ "version": "5.46.1",
3
3
  "repository": {
4
4
  "type": "git",
5
5
  "url": "git+https://github.com/fern-api/fern.git",