@postman/sdk-config 0.10.1 → 0.11.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 (36) hide show
  1. package/README.md +6 -0
  2. package/dist/index.cjs +697 -34
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +3 -3
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.js +683 -35
  7. package/dist/index.js.map +1 -1
  8. package/dist/sdk-config/index.cjs +649 -34
  9. package/dist/sdk-config/index.cjs.map +1 -1
  10. package/dist/sdk-config/index.d.cts +2 -2
  11. package/dist/sdk-config/index.d.ts +2 -2
  12. package/dist/sdk-config/index.js +642 -35
  13. package/dist/sdk-config/index.js.map +1 -1
  14. package/dist/sdk-config/v1/index.cjs +649 -34
  15. package/dist/sdk-config/v1/index.cjs.map +1 -1
  16. package/dist/sdk-config/v1/index.d.cts +2858 -2377
  17. package/dist/sdk-config/v1/index.d.ts +2858 -2377
  18. package/dist/sdk-config/v1/index.js +642 -35
  19. package/dist/sdk-config/v1/index.js.map +1 -1
  20. package/dist/sdk-config-ir/index.cjs +247 -4
  21. package/dist/sdk-config-ir/index.cjs.map +1 -1
  22. package/dist/sdk-config-ir/index.d.cts +2 -2
  23. package/dist/sdk-config-ir/index.d.ts +2 -2
  24. package/dist/sdk-config-ir/index.js +241 -5
  25. package/dist/sdk-config-ir/index.js.map +1 -1
  26. package/dist/sdk-config-ir/v1/index.cjs +247 -4
  27. package/dist/sdk-config-ir/v1/index.cjs.map +1 -1
  28. package/dist/sdk-config-ir/v1/index.d.cts +3109 -2960
  29. package/dist/sdk-config-ir/v1/index.d.ts +3109 -2960
  30. package/dist/sdk-config-ir/v1/index.js +241 -5
  31. package/dist/sdk-config-ir/v1/index.js.map +1 -1
  32. package/dist/{typescript-Chge0X4f.d.cts → typescript-CsAa1VSy.d.cts} +81 -1
  33. package/dist/{typescript-Chge0X4f.d.ts → typescript-CsAa1VSy.d.ts} +81 -1
  34. package/package.json +1 -1
  35. package/src/sdk-config/v1/README.md +183 -1
  36. package/src/sdk-config-ir/v1/README.md +29 -1
package/dist/index.js CHANGED
@@ -125,13 +125,21 @@ var oauthScopeSchema = z.strictObject({
125
125
  name: nonEmptyStringSchema.describe("The OAuth scope value requested from the provider."),
126
126
  description: z.string().optional().describe("A description of the OAuth scope included in generated SDK documentation.")
127
127
  });
128
+ var publicClientIdSchema = nonEmptyStringSchema.refine((value) => value.trim() === value, {
129
+ message: "must not have leading or trailing whitespace"
130
+ }).refine((value) => !value.includes("${"), {
131
+ message: "must be a literal public client ID, not an environment expression"
132
+ });
128
133
  var oauthFlowSchema = z.discriminatedUnion("type", [
129
134
  z.strictObject({
130
135
  type: z.literal("authorization-code").describe("Uses the OAuth authorization code flow."),
131
136
  authorizationUrl: nonEmptyStringSchema.describe("The URL used to authorize the client."),
132
137
  tokenUrl: nonEmptyStringSchema.describe("The URL used to exchange a code for tokens."),
133
138
  refreshUrl: nonEmptyStringSchema.optional().describe("The URL used to refresh access tokens."),
134
- scopes: z.array(oauthScopeSchema).optional().describe("The scopes supported by this flow.")
139
+ scopes: z.array(oauthScopeSchema).optional().describe("The scopes supported by this flow."),
140
+ clientId: publicClientIdSchema.optional().describe(
141
+ "The public, non-secret OAuth client ID that generated interactive login flows, such as CLI browser login with PKCE, send to the authorization and token endpoints. It is embedded verbatim in generated code, so it must never be a client secret."
142
+ )
135
143
  }),
136
144
  z.strictObject({
137
145
  type: z.literal("client-credentials").describe("Uses the OAuth client credentials flow."),
@@ -447,8 +455,102 @@ var packageConfigSchema = z.strictObject({
447
455
  projectUrls: z.array(projectUrlSchema).optional().describe("Labeled project links for package metadata."),
448
456
  extraDependencies: z.array(dependencySchema).optional().describe("Additional runtime dependencies added to the generated package."),
449
457
  extraDevDependencies: z.array(dependencySchema).optional().describe("Additional development dependencies added to the generated package."),
450
- extraPeerDependencies: z.array(dependencySchema).optional().describe("Additional peer dependencies added to the generated package.")
458
+ extraPeerDependencies: z.array(dependencySchema).optional().describe("Additional peer dependencies added to the generated package."),
459
+ extraPeerDependenciesMeta: z.record(nonEmptyStringSchema, jsonObjectSchema).optional().describe(
460
+ "npm peerDependenciesMeta entries keyed by peer dependency name, such as { optional: true }."
461
+ )
451
462
  });
463
+ var GITHUB_ACTIONS_SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
464
+ var GITHUB_REPOSITORY_SLUG_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
465
+ var HOMEBREW_FORMULA_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
466
+ var githubActionsSecretNameSchema = z.string({ error: "must be the name of a GitHub Actions secret" }).regex(GITHUB_ACTIONS_SECRET_NAME_PATTERN, {
467
+ error: "must be the name of a GitHub Actions secret: start with an uppercase letter and contain only A-Z, 0-9, or _. Secret values and ${...} expressions are not accepted."
468
+ }).refine((value) => value !== "GITHUB_TOKEN", {
469
+ error: "GITHUB_TOKEN cannot be used: it is the workflow's built-in token, scoped to the generated repository. Store the credential under a different secret name."
470
+ });
471
+ var githubRepositorySlugSchema = z.string({ error: 'must be a GitHub repository in "<owner>/<repo>" form' }).regex(GITHUB_REPOSITORY_SLUG_PATTERN, {
472
+ error: 'must be a GitHub repository in "<owner>/<repo>" form, for example "acme/homebrew-tap"'
473
+ });
474
+ var homebrewFormulaNameSchema = z.string({ error: "must be a Homebrew formula name" }).regex(HOMEBREW_FORMULA_NAME_PATTERN, {
475
+ error: "must be a Homebrew formula name: start with a lowercase letter and contain only a-z, 0-9, or -"
476
+ });
477
+ var cliDistributionGithubAppSchema = z.strictObject({
478
+ appIdSecretName: githubActionsSecretNameSchema.describe(
479
+ "The name of the GitHub Actions secret holding the GitHub App ID or client ID."
480
+ ),
481
+ privateKeySecretName: githubActionsSecretNameSchema.describe(
482
+ "The name of the GitHub Actions secret holding the GitHub App private key in PEM form."
483
+ )
484
+ });
485
+ var channelTokenSecretNameDescription = "The name of the GitHub Actions secret holding a token with write access to the channel repository. Takes precedence over githubApp for this channel.";
486
+ var cliDistributionHomebrewSchema = z.strictObject({
487
+ tap: githubRepositorySlugSchema.describe(
488
+ 'The Homebrew tap repository in "<owner>/<repo>" form that receives the formula.'
489
+ ),
490
+ formula: homebrewFormulaNameSchema.optional().describe("The Homebrew formula name; the generator defaults it to the CLI binary name."),
491
+ tokenSecretName: githubActionsSecretNameSchema.optional().describe(channelTokenSecretNameDescription)
492
+ });
493
+ var cliDistributionScoopSchema = z.strictObject({
494
+ bucket: githubRepositorySlugSchema.describe(
495
+ 'The Scoop bucket repository in "<owner>/<repo>" form that receives the manifest.'
496
+ ),
497
+ tokenSecretName: githubActionsSecretNameSchema.optional().describe(channelTokenSecretNameDescription)
498
+ });
499
+ var cliDistributionConfigSchema = z.strictObject({
500
+ githubApp: cliDistributionGithubAppSchema.optional().describe(
501
+ "A GitHub App used to mint short-lived tokens for every channel without its own tokenSecretName."
502
+ ),
503
+ homebrew: cliDistributionHomebrewSchema.optional().describe("Publishes a Homebrew formula to a tap repository on each tagged release."),
504
+ scoop: cliDistributionScoopSchema.optional().describe("Publishes a Scoop manifest to a bucket repository on each tagged release.")
505
+ }).superRefine((distribution, context) => {
506
+ if (distribution.homebrew !== void 0 || distribution.scoop !== void 0) {
507
+ return;
508
+ }
509
+ if (distribution.githubApp !== void 0) {
510
+ context.addIssue({
511
+ code: "custom",
512
+ message: "githubApp requires at least one distribution channel: homebrew or scoop",
513
+ path: ["githubApp"]
514
+ });
515
+ return;
516
+ }
517
+ context.addIssue({
518
+ code: "custom",
519
+ message: "distribution must configure at least one channel: homebrew or scoop"
520
+ });
521
+ });
522
+ var CLI_DISTRIBUTION_REQUIRES_GITHUB_DELIVERY_MESSAGE = 'distribution requires output.delivery "github": Homebrew and Scoop publish from a tagged GitHub Release produced by the generated release workflow. Set delivery to "github" or remove distribution.';
523
+ var CLI_PUBLISH_REQUIRES_GITHUB_DELIVERY_MESSAGE = 'package publication for target language "cli" requires output.delivery "github": the CLI generator publishes npm packages only from its generated GitHub workflow.';
524
+ var CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE = "CLI_DISTRIBUTION_NON_RELEASE_MODE";
525
+ var nonReleaseGithubModes = /* @__PURE__ */ new Set(["pull-request", "push", "commit"]);
526
+ function cliDistributionModeDiagnostic(output, outputPath) {
527
+ const mode = output?.github?.mode;
528
+ if (output?.distribution === void 0 || mode === void 0 || !nonReleaseGithubModes.has(mode)) {
529
+ return void 0;
530
+ }
531
+ return {
532
+ code: CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE,
533
+ severity: "warning",
534
+ path: [...outputPath, "github", "mode"],
535
+ reason: `GitHub mode "${mode}" does not create a release tag, so Homebrew and Scoop publish only after a semantic-version tag is pushed to the repository.`,
536
+ suggestedAction: 'Use mode "release" or "commit-and-release", or push a version tag after merging generated changes.'
537
+ };
538
+ }
539
+
540
+ // src/sdk-config/v1/diagnostics.ts
541
+ function getSdkConfigV1Diagnostics(config) {
542
+ return config.targets.flatMap((target, targetIndex) => {
543
+ if (target.language !== "cli") {
544
+ return [];
545
+ }
546
+ const diagnostic = cliDistributionModeDiagnostic(target.output, [
547
+ "targets",
548
+ targetIndex,
549
+ "output"
550
+ ]);
551
+ return diagnostic ? [diagnostic] : [];
552
+ });
553
+ }
452
554
  var httpMethodSchema = z.enum([
453
555
  "GET",
454
556
  "POST",
@@ -653,6 +755,17 @@ function stringValue(value) {
653
755
  function stringArrayValue(value) {
654
756
  return Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0) ? value : void 0;
655
757
  }
758
+ var canonicalLicenseTypes = {
759
+ mit: "MIT",
760
+ apache: "APACHE",
761
+ "apache-2.0": "APACHE",
762
+ "gpl-3.0": "GPL3",
763
+ "gpl-3.0-only": "GPL3",
764
+ unlicensed: "UNLICENSED"
765
+ };
766
+ function canonicalLicenseType(value) {
767
+ return canonicalLicenseTypes[value.trim().toLowerCase()];
768
+ }
656
769
  function stableJson(value) {
657
770
  return JSON.stringify(sortKeys(value));
658
771
  }
@@ -663,6 +776,280 @@ function sortKeys(value) {
663
776
  Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, sortKeys(child)])
664
777
  );
665
778
  }
779
+ var ALPHANUMERIC_PATTERN = /[A-Za-z0-9]/;
780
+ var ROOT_GROUP_PATTERN = /^[a-z][a-z0-9_-]*$/;
781
+ var KEBAB_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
782
+ var DOTTED_COMMAND_PATH_PATTERN = /^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/;
783
+ var FIELD_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
784
+ var RESERVED_USER_AGENT_SUFFIX_FLAGS = /* @__PURE__ */ new Set([
785
+ "params",
786
+ "output",
787
+ "json",
788
+ "format",
789
+ "dry-run",
790
+ "base-url",
791
+ "page-all",
792
+ "page-limit",
793
+ "page-delay",
794
+ "no-pager",
795
+ "no-extract",
796
+ "no-retry",
797
+ "no-stream",
798
+ "quiet",
799
+ "query",
800
+ "help",
801
+ "debug",
802
+ "schema"
803
+ ]);
804
+ var RESERVED_PROFILES_COMMAND_NAMES = /* @__PURE__ */ new Set([
805
+ "auth",
806
+ "completion",
807
+ "man",
808
+ "errors",
809
+ "generate-skills",
810
+ "help"
811
+ ]);
812
+ var binaryNameSchema = nonEmptyStringSchema.regex(ALPHANUMERIC_PATTERN, {
813
+ message: "binaryName must contain at least one ASCII letter or digit"
814
+ });
815
+ var rootGroupSchema = z.string().regex(ROOT_GROUP_PATTERN, {
816
+ message: "rootGroup must start with a lowercase letter and contain only lowercase letters, digits, hyphens, and underscores"
817
+ });
818
+ var userAgentSuffixFlagSchema = z.string().superRefine((value, context) => {
819
+ if (value.startsWith("-")) {
820
+ context.addIssue({
821
+ code: "custom",
822
+ message: 'userAgentSuffixFlag must be the long flag name without the leading "--"'
823
+ });
824
+ } else if (!KEBAB_NAME_PATTERN.test(value)) {
825
+ context.addIssue({
826
+ code: "custom",
827
+ message: "userAgentSuffixFlag must start with a lowercase letter and contain only lowercase letters, digits, and hyphens"
828
+ });
829
+ } else if (RESERVED_USER_AGENT_SUFFIX_FLAGS.has(value)) {
830
+ context.addIssue({
831
+ code: "custom",
832
+ message: `userAgentSuffixFlag "${value}" collides with a built-in global flag`
833
+ });
834
+ }
835
+ });
836
+ var profilesCommandNameSchema = z.string().superRefine((value, context) => {
837
+ if (!KEBAB_NAME_PATTERN.test(value)) {
838
+ context.addIssue({
839
+ code: "custom",
840
+ message: "profiles.commandName must start with a lowercase letter and contain only lowercase letters, digits, and hyphens"
841
+ });
842
+ } else if (RESERVED_PROFILES_COMMAND_NAMES.has(value)) {
843
+ context.addIssue({
844
+ code: "custom",
845
+ message: `profiles.commandName "${value}" collides with a built-in command group`
846
+ });
847
+ }
848
+ });
849
+ function dottedCommandPathSchema(field) {
850
+ return z.string().regex(DOTTED_COMMAND_PATH_PATTERN, {
851
+ message: `${field} must be a dotted command path such as "iam.keys.create"`
852
+ });
853
+ }
854
+ function fieldNameMapSchema(field) {
855
+ const message = `${field} keys and values must be field names containing only [A-Za-z0-9_.-]`;
856
+ return z.record(z.string(), z.string().regex(FIELD_NAME_PATTERN, { message })).superRefine((value, context) => {
857
+ for (const key of Object.keys(value)) {
858
+ if (!FIELD_NAME_PATTERN.test(key)) {
859
+ context.addIssue({ code: "custom", message, path: [key] });
860
+ }
861
+ }
862
+ });
863
+ }
864
+ var profilesProvisionOperationSchema = z.strictObject({
865
+ operation: dottedCommandPathSchema("profiles.provisionOperation.operation").describe(
866
+ "The dotted command path of the operation that mints a profile credential."
867
+ ),
868
+ credential: fieldNameMapSchema("profiles.provisionOperation.credential").refine((value) => Object.keys(value).length > 0, {
869
+ message: "profiles.provisionOperation.credential must map at least one credential field"
870
+ }).describe("Maps each stored credential field to the response field that supplies it."),
871
+ revokeParameters: fieldNameMapSchema("profiles.provisionOperation.revokeParameters").optional().describe("Maps each revoke-operation parameter to the response field that supplies it.")
872
+ });
873
+ var profilesConfigSchema = z.strictObject({
874
+ enabled: z.boolean().describe("Whether the CLI emits profile commands and the global --profile flag."),
875
+ commandName: profilesCommandNameSchema.optional().describe('The profile command group name. The generator defaults to "profiles".'),
876
+ revokeOperation: dottedCommandPathSchema("profiles.revokeOperation").optional().describe('The dotted command path called by "profiles remove --revoke".'),
877
+ provisionOperation: profilesProvisionOperationSchema.optional().describe('The operation called by "profiles create --provision" and its response mapping.')
878
+ });
879
+ var cliGenerationConfigSchema = z.strictObject({
880
+ paginationParameters: z.array(nonEmptyStringSchema).optional().describe("Request parameter names treated as pagination controls by the CLI."),
881
+ skills: z.boolean().optional().describe("Whether the generated CLI includes skills that teach AI agents to invoke commands."),
882
+ binaryName: binaryNameSchema.optional().describe(
883
+ "The CLI executable name. The generator kebab-cases it and also uses it for the source directory and env-var prefix."
884
+ ),
885
+ customCommands: z.boolean().optional().describe("Whether hand-written command support is generated. The generator defaults to true."),
886
+ rootGroup: rootGroupSchema.optional().describe("A command group that every spec-derived command is nested under."),
887
+ profiles: profilesConfigSchema.optional().describe("Named profile command configuration."),
888
+ splitTypeCrates: z.boolean().optional().describe("Whether generated types are split into one crate per API behind a facade crate."),
889
+ userAgentSuffixFlag: userAgentSuffixFlagSchema.optional().describe('The long flag name, without "--", that appends a product token to the User-Agent.')
890
+ });
891
+
892
+ // src/sdk-config/v1/fern-config-mapper-cli.ts
893
+ var CLI_GENERATION_FIELDS = [
894
+ "binaryName",
895
+ "customCommands",
896
+ "rootGroup",
897
+ "profiles",
898
+ "splitTypeCrates",
899
+ "userAgentSuffixFlag"
900
+ ];
901
+ var PROFILES_FIELDS = ["enabled", "commandName", "revokeOperation"];
902
+ var PROVISION_OPERATION_FIELDS = ["operation", "credential", "revokeParameters"];
903
+ function mapCliGeneration(config, state, basePath, targetIndex) {
904
+ const generation = {};
905
+ for (const field of CLI_GENERATION_FIELDS) {
906
+ const raw = ownValue(config, field);
907
+ if (raw === void 0) continue;
908
+ const fieldPath = [...basePath, field];
909
+ const { value, consumedPaths } = field === "profiles" && isObject(raw) ? pickProfiles(raw, fieldPath) : { value: raw, consumedPaths: [fieldPath] };
910
+ const parsed = cliGenerationConfigSchema.shape[field].safeParse(value);
911
+ consumedPaths.forEach((path) => consume(state, path));
912
+ if (parsed.success) {
913
+ generation[field] = structuredClone(value);
914
+ continue;
915
+ }
916
+ const diagnostics = parsed.error.issues.map(
917
+ (issue) => invalidValueDiagnostic(field, fieldPath, issue.path, issue.message, targetIndex)
918
+ );
919
+ (state.valueDiagnostics ??= []).push(...diagnostics);
920
+ }
921
+ return generation;
922
+ }
923
+ function pickProfiles(profiles, path) {
924
+ const picked = pickKnown(profiles, PROFILES_FIELDS, path);
925
+ const provisionOperation = ownValue(profiles, "provisionOperation");
926
+ const provisionPath = [...path, "provisionOperation"];
927
+ if (isObject(provisionOperation)) {
928
+ const provision = pickKnown(provisionOperation, PROVISION_OPERATION_FIELDS, provisionPath);
929
+ picked.value.provisionOperation = provision.value;
930
+ picked.consumedPaths.push(...provision.consumedPaths);
931
+ } else if (provisionOperation !== void 0) {
932
+ picked.value.provisionOperation = provisionOperation;
933
+ picked.consumedPaths.push(provisionPath);
934
+ }
935
+ return { value: { enabled: false, ...picked.value }, consumedPaths: picked.consumedPaths };
936
+ }
937
+ function pickKnown(value, fields, path) {
938
+ const picked = {};
939
+ const consumedPaths = [];
940
+ for (const field of fields) {
941
+ const child = ownValue(value, field);
942
+ if (child === void 0) continue;
943
+ picked[field] = child;
944
+ consumedPaths.push([...path, field]);
945
+ }
946
+ return { value: picked, consumedPaths };
947
+ }
948
+ function ownValue(value, key) {
949
+ return Object.hasOwn(value, key) ? value[key] : void 0;
950
+ }
951
+ function invalidValueDiagnostic(field, fieldPath, issuePath, message, targetIndex) {
952
+ const relative = issuePath.map((part) => typeof part === "symbol" ? String(part) : part);
953
+ const sdkConfigField = ["targets", targetIndex, "generation", field];
954
+ return {
955
+ code: "FERN_CONFIG_VALUE_INVALID",
956
+ severity: "warning",
957
+ path: [...fieldPath, ...relative],
958
+ reason: `CLI setting ${field} was not migrated because SDK Config rejects it: ${message}`,
959
+ sdkConfigPath: [...sdkConfigField, ...relative],
960
+ suggestedAction: `Correct the value, then set ${sdkConfigField.join(".")} manually.`
961
+ };
962
+ }
963
+
964
+ // src/sdk-config/v1/fern-config-mapper-cli-distribution.ts
965
+ var DISTRIBUTION_KEYS = {
966
+ githubApp: { appIdSecret: "appIdSecretName", privateKeySecret: "privateKeySecretName" },
967
+ homebrew: { tap: "tap", formula: "formula", tokenEnvironmentVariable: "tokenSecretName" },
968
+ scoop: { bucket: "bucket", tokenEnvironmentVariable: "tokenSecretName" }
969
+ };
970
+ function mapCliDistribution(config, state, basePath, targetIndex) {
971
+ if (!Object.hasOwn(config, "distribution")) return;
972
+ const raw = config.distribution;
973
+ const path = [...basePath, "distribution"];
974
+ const translated = isObject(raw) ? translateDistribution(raw, path) : { value: raw, consumedPaths: [path], legacyKeys: /* @__PURE__ */ new Map() };
975
+ const parsed = cliDistributionConfigSchema.safeParse(translated.value);
976
+ translated.consumedPaths.forEach((consumedPath) => consume(state, consumedPath));
977
+ if (parsed.success) {
978
+ state.cliDistribution = { value: parsed.data, path };
979
+ return;
980
+ }
981
+ const diagnostics = parsed.error.issues.map(
982
+ (issue) => invalidDistributionDiagnostic(issue, path, translated.legacyKeys, targetIndex)
983
+ );
984
+ const reportedPaths = /* @__PURE__ */ new Set();
985
+ (state.valueDiagnostics ??= []).push(
986
+ ...diagnostics.filter(({ path: diagnosticPath }) => {
987
+ const key = diagnosticPath.join(".");
988
+ if (reportedPaths.has(key)) return false;
989
+ reportedPaths.add(key);
990
+ return true;
991
+ })
992
+ );
993
+ }
994
+ function translateDistribution(distribution, path) {
995
+ const value = {};
996
+ const consumedPaths = [];
997
+ const legacyKeys = /* @__PURE__ */ new Map();
998
+ for (const [block, keys] of Object.entries(DISTRIBUTION_KEYS)) {
999
+ if (!Object.hasOwn(distribution, block)) continue;
1000
+ const blockValue = distribution[block];
1001
+ const blockPath = [...path, block];
1002
+ if (!isObject(blockValue)) {
1003
+ value[block] = blockValue;
1004
+ consumedPaths.push(blockPath);
1005
+ continue;
1006
+ }
1007
+ const translatedBlock = {};
1008
+ for (const [legacyKey, sdkConfigKey] of Object.entries(keys)) {
1009
+ if (!Object.hasOwn(blockValue, legacyKey)) continue;
1010
+ translatedBlock[sdkConfigKey] = blockValue[legacyKey];
1011
+ consumedPaths.push([...blockPath, legacyKey]);
1012
+ legacyKeys.set(`${block}.${sdkConfigKey}`, legacyKey);
1013
+ }
1014
+ value[block] = translatedBlock;
1015
+ }
1016
+ return { value, consumedPaths, legacyKeys };
1017
+ }
1018
+ function invalidDistributionDiagnostic(issue, path, legacyKeys, targetIndex) {
1019
+ const relative = issue.path.map((part) => typeof part === "symbol" ? String(part) : part);
1020
+ const [block, field] = relative;
1021
+ const legacyField = typeof block === "string" && typeof field === "string" ? legacyKeys.get(`${block}.${field}`) : void 0;
1022
+ const legacyRelative = typeof block === "string" && legacyField !== void 0 ? [block, legacyField] : relative;
1023
+ const sdkConfigField = ["targets", targetIndex, "output", "distribution"];
1024
+ return {
1025
+ code: "FERN_CONFIG_VALUE_INVALID",
1026
+ severity: "warning",
1027
+ path: [...path, ...legacyRelative],
1028
+ reason: `CLI distribution was not migrated because SDK Config rejects it: ${issue.message}`,
1029
+ sdkConfigPath: [...sdkConfigField, ...relative],
1030
+ suggestedAction: `Correct the value, then set ${sdkConfigField.join(".")} manually.`
1031
+ };
1032
+ }
1033
+ function applyCliDistribution(output, pending, targetIndex) {
1034
+ if (pending === void 0) return { output, diagnostics: [] };
1035
+ if (output.delivery === "github") {
1036
+ return { output: { ...output, distribution: pending.value }, diagnostics: [] };
1037
+ }
1038
+ const outputPath = ["targets", targetIndex, "output"];
1039
+ return {
1040
+ output,
1041
+ diagnostics: [
1042
+ {
1043
+ code: "FERN_CLI_DISTRIBUTION_REQUIRES_GITHUB_OUTPUT",
1044
+ severity: "warning",
1045
+ path: pending.path,
1046
+ reason: `The CLI generator ignores distribution for "${output.delivery}" output, and SDK Config accepts distribution only with output.delivery "github", so it was not migrated`,
1047
+ sdkConfigPath: [...outputPath, "distribution"],
1048
+ suggestedAction: `Set ${outputPath.join(".")}.delivery to "github" for the CLI repository and add ${outputPath.join(".")}.distribution manually, or remove distribution.`
1049
+ }
1050
+ ]
1051
+ };
1052
+ }
666
1053
 
667
1054
  // src/sdk-config/v1/fern-config-mapper-output.ts
668
1055
  function mapOutput(generator, language, index) {
@@ -873,11 +1260,10 @@ function githubOutput(value, publishInfo, rawOutput, language, sourcePath, state
873
1260
  ...optionalObject("package", { ...publication?.package, ...licensePackage })
874
1261
  };
875
1262
  }
876
- var basicLicenseTypes = { mit: "MIT", apache: "APACHE" };
877
1263
  function mapGithubLicense(value, sourcePath, state) {
878
1264
  if (!isObject(value) || stringValue(value.type) !== "basic") return {};
879
1265
  const id = stringValue(value.id);
880
- const type = id ? basicLicenseTypes[id.toLowerCase()] : void 0;
1266
+ const type = id ? canonicalLicenseType(id) : void 0;
881
1267
  if (!type) return {};
882
1268
  consume(state, [...sourcePath, "type"]);
883
1269
  consume(state, [...sourcePath, "id"]);
@@ -1444,6 +1830,114 @@ function outputDiagnostic(path, index) {
1444
1830
  suggestedAction: resolvedGuidance?.suggestedAction ?? "Review this output setting and preserve it outside SDK Config when no equivalent exists."
1445
1831
  };
1446
1832
  }
1833
+
1834
+ // src/sdk-config/v1/package.ts
1835
+ var sdkConfigV1DependencySchema = dependencySchema;
1836
+ var sdkConfigV1PackageConfigSchema = packageConfigSchema;
1837
+
1838
+ // src/sdk-config/v1/fern-config-mapper-package.ts
1839
+ var emailSchema = z.email();
1840
+ function mapCliPackageIdentity(identity, state, targetIndex) {
1841
+ mapIdentityField(identity, "homepage", stringValue, state);
1842
+ mapIdentityField(identity, "keywords", (value) => stringArrayValue(value)?.slice(), state);
1843
+ mapCargoAuthors(identity, state, targetIndex);
1844
+ mapIdentityField(identity, "license", licenseValue, state);
1845
+ }
1846
+ function mapIdentityField(identity, field, read, state) {
1847
+ const located = locate(identity.value, [field], identity.path);
1848
+ const value = located && read(located.value);
1849
+ if (located && value !== void 0) addPackageFallback(state, field, value, located.path);
1850
+ }
1851
+ function mapLicenseFallback(config, aliases, state, basePath) {
1852
+ const located = locate(config, aliases, basePath);
1853
+ const license = located && licenseValue(located.value);
1854
+ if (located && license) addPackageFallback(state, "license", license, located.path);
1855
+ }
1856
+ function resolveTargetPackage(targetPackage, fallbacks, generator, delivery, index) {
1857
+ const diagnostics = [];
1858
+ for (const fallback of fallbacks) {
1859
+ applyFallback(targetPackage, fallback, index, diagnostics);
1860
+ }
1861
+ if (delivery !== "github") {
1862
+ applyMetadataLicense(targetPackage, generator.raw, index, diagnostics);
1863
+ }
1864
+ return diagnostics;
1865
+ }
1866
+ function applyFallback(targetPackage, { field, value, path }, index, diagnostics) {
1867
+ const existing = targetPackage[field];
1868
+ if (existing === void 0) {
1869
+ targetPackage[field] = value;
1870
+ } else if (stableJson(existing) !== stableJson(value)) {
1871
+ diagnostics.push({
1872
+ code: "FERN_PACKAGE_VALUE_CONFLICT",
1873
+ severity: "warning",
1874
+ path,
1875
+ reason: `Fern value conflicts with the package.${field} already migrated for this target, which is kept`,
1876
+ sdkConfigPath: ["targets", index, "package", field],
1877
+ suggestedAction: `Choose the intended value and set targets.${index}.package.${field} manually.`
1878
+ });
1879
+ }
1880
+ }
1881
+ function applyMetadataLicense(targetPackage, raw, index, diagnostics) {
1882
+ const metadata = isObject(raw) && isObject(raw.metadata) ? raw.metadata : void 0;
1883
+ if (metadata?.license === void 0 || metadata.license === null) return;
1884
+ const path = ["group", "generators", index, "metadata", "license"];
1885
+ const license = licenseValue(metadata.license);
1886
+ if (license) {
1887
+ applyFallback(targetPackage, { field: "license", value: license, path }, index, diagnostics);
1888
+ return;
1889
+ }
1890
+ diagnostics.push({
1891
+ code: "FERN_CONFIG_FIELD_UNSUPPORTED",
1892
+ severity: "warning",
1893
+ path,
1894
+ reason: isObject(metadata.license) ? "A custom license file cannot be migrated automatically" : "metadata.license is not a license identifier",
1895
+ sdkConfigPath: ["targets", index, "package", "license"],
1896
+ suggestedAction: `Set targets.${index}.package.license manually.`
1897
+ });
1898
+ }
1899
+ function mapCargoAuthors(identity, state, targetIndex) {
1900
+ const located = locate(identity.value, ["authors"], identity.path);
1901
+ const entries = located && stringArrayValue(located.value);
1902
+ if (!located || !entries) return;
1903
+ consume(state, located.path);
1904
+ const authors = entries.map(parseCargoAuthor);
1905
+ const parsed = sdkConfigV1PackageConfigSchema.shape.authors.safeParse(authors);
1906
+ const invalid = parsed.success ? [] : [...new Set(parsed.error.issues.map((issue) => issue.path[0]))].filter(
1907
+ (index) => typeof index === "number"
1908
+ );
1909
+ if (invalid.length === 0) {
1910
+ addPackageFallback(state, "authors", authors, located.path);
1911
+ return;
1912
+ }
1913
+ const authorsPath = ["targets", targetIndex, "package", "authors"];
1914
+ (state.valueDiagnostics ??= []).push(
1915
+ ...invalid.map((entry) => ({
1916
+ code: "FERN_CONFIG_VALUE_INVALID",
1917
+ severity: "warning",
1918
+ path: [...located.path, entry],
1919
+ reason: 'CLI packageIdentity.authors was not migrated because this entry is not "Name" or "Name <email>" with a valid email',
1920
+ sdkConfigPath: authorsPath,
1921
+ suggestedAction: `Correct the entry, then set ${authorsPath.join(".")} manually.`
1922
+ }))
1923
+ );
1924
+ }
1925
+ function parseCargoAuthor(entry) {
1926
+ const trimmed = entry.trim();
1927
+ const match = /^(.*?)\s*<([^<>]*)>$/.exec(trimmed);
1928
+ if (!match) return { name: trimmed };
1929
+ const [, name = "", email = ""] = match;
1930
+ if (name !== "" && !emailSchema.safeParse(email).success) return { name: trimmed };
1931
+ return { name, email };
1932
+ }
1933
+ function licenseValue(value) {
1934
+ const license = stringValue(value)?.trim();
1935
+ return license ? { type: canonicalLicenseType(license) ?? license } : void 0;
1936
+ }
1937
+ function addPackageFallback(state, field, value, path) {
1938
+ consume(state, path);
1939
+ (state.packageFallbacks ??= []).push({ field, value, path });
1940
+ }
1447
1941
  var OMIT = /* @__PURE__ */ Symbol("omit");
1448
1942
  function inputJsonSchema(schema) {
1449
1943
  return z.toJSONSchema(schema, {
@@ -1713,7 +2207,16 @@ var sdkConfigV1PublishConfigSchema = z.discriminatedUnion("registry", [
1713
2207
  z.strictObject({
1714
2208
  registry: z.literal("npm").describe("Publishes the package to an npm-compatible registry."),
1715
2209
  token: nonEmptyStringSchema.optional().describe("The npm registry token, or an environment expression that resolves to it."),
2210
+ trustedPublishing: z.boolean().optional().describe("Whether npm publication uses trusted publishing (OIDC) instead of a token."),
1716
2211
  ...commonPublishShape
2212
+ }).superRefine((publish, context) => {
2213
+ if (publish.trustedPublishing === true && publish.token !== void 0) {
2214
+ context.addIssue({
2215
+ code: "custom",
2216
+ message: "token must be omitted when trustedPublishing is true",
2217
+ path: ["token"]
2218
+ });
2219
+ }
1717
2220
  }),
1718
2221
  z.strictObject({
1719
2222
  registry: z.literal("pypi").describe("Publishes the package to a PyPI-compatible registry."),
@@ -1791,10 +2294,29 @@ var sdkConfigV1OutputConfigSchema = z.discriminatedUnion(
1791
2294
  [filesOutputSchema, zipOutputSchema, githubDeliverySchema],
1792
2295
  { error: 'output.delivery must be one of "files", "zip", or "github"' }
1793
2296
  );
1794
-
1795
- // src/sdk-config/v1/package.ts
1796
- var sdkConfigV1DependencySchema = dependencySchema;
1797
- var sdkConfigV1PackageConfigSchema = packageConfigSchema;
2297
+ var cliDistributionShape = {
2298
+ distribution: cliDistributionConfigSchema.optional().describe(
2299
+ "CLI release channels published by the generated release workflow; credentials are GitHub Actions secret names."
2300
+ )
2301
+ };
2302
+ function rejectDistributionWithoutGithub(output, context) {
2303
+ if (output.distribution !== void 0) {
2304
+ context.addIssue({
2305
+ code: "custom",
2306
+ message: CLI_DISTRIBUTION_REQUIRES_GITHUB_DELIVERY_MESSAGE,
2307
+ path: ["distribution"]
2308
+ });
2309
+ }
2310
+ }
2311
+ var sdkConfigV1CliOutputConfigSchema = z.discriminatedUnion(
2312
+ "delivery",
2313
+ [
2314
+ filesOutputSchema.extend(cliDistributionShape).superRefine(rejectDistributionWithoutGithub),
2315
+ zipOutputSchema.extend(cliDistributionShape).superRefine(rejectDistributionWithoutGithub),
2316
+ githubDeliverySchema.extend(cliDistributionShape)
2317
+ ],
2318
+ { error: 'output.delivery must be one of "files", "zip", or "github"' }
2319
+ );
1798
2320
  var sdkConfigV1ReplayConfigSchema = z.strictObject({
1799
2321
  enabled: z.boolean().describe("Whether regeneration reapplies customer-authored repository changes.")
1800
2322
  });
@@ -1871,10 +2393,6 @@ var sdkConfigV1SourceConfigSchema = z.strictObject({
1871
2393
  seenIds.add(id);
1872
2394
  });
1873
2395
  });
1874
- var cliGenerationConfigSchema = z.strictObject({
1875
- paginationParameters: z.array(nonEmptyStringSchema).optional().describe("Request parameter names treated as pagination controls by the CLI."),
1876
- skills: z.boolean().optional().describe("Whether the generated CLI includes skills that teach AI agents to invoke commands.")
1877
- });
1878
2396
  var csharpGenerationConfigSchema = z.strictObject({
1879
2397
  useOptionalWrapper: z.boolean().optional().describe("Whether optional values use an explicit Optional wrapper."),
1880
2398
  simplifyObjectDictionaries: z.boolean().optional().describe("Whether object schemas with arbitrary values use simplified dictionary types."),
@@ -2127,7 +2645,10 @@ var sdkConfigV1TargetSchema = z.discriminatedUnion("language", [
2127
2645
  z.strictObject({
2128
2646
  language: z.literal("cli").describe("Generates a command-line interface."),
2129
2647
  generation: sdkConfigV1GenerationConfigOverrideSchema.extend(cliGenerationConfigSchema.shape).optional().describe("Common and CLI-specific generation settings for this target."),
2130
- ...targetOverrideShape
2648
+ ...targetOverrideShape,
2649
+ output: sdkConfigV1CliOutputConfigSchema.optional().describe(
2650
+ "Output configuration, including optional CLI distribution channels, that replaces the root output for this target."
2651
+ )
2131
2652
  }),
2132
2653
  z.strictObject({
2133
2654
  language: z.literal("mcp").describe("Generates an MCP server."),
@@ -2144,7 +2665,7 @@ var sdkConfigV1TargetSchema = z.discriminatedUnion("language", [
2144
2665
  // src/sdk-config/v1/sdk-config-v1.ts
2145
2666
  var SDK_CONFIG_V1_SCHEMA_VERSION = "sdk-config/v1";
2146
2667
  var publishRegistryLanguages = {
2147
- npm: ["typescript", "mcp"],
2668
+ npm: ["typescript", "mcp", "cli"],
2148
2669
  pypi: ["python"],
2149
2670
  maven: ["java", "kotlin"],
2150
2671
  nuget: ["csharp"],
@@ -2221,6 +2742,13 @@ function validateTargetPublishing(target, targetIndex, globalPackage, globalOutp
2221
2742
  });
2222
2743
  return;
2223
2744
  }
2745
+ if (target.language === "cli" && output.delivery !== "github") {
2746
+ context.addIssue({
2747
+ code: "custom",
2748
+ message: CLI_PUBLISH_REQUIRES_GITHUB_DELIVERY_MESSAGE,
2749
+ path: target.output ? ["targets", targetIndex, "output", "delivery"] : ["output", "delivery"]
2750
+ });
2751
+ }
2224
2752
  validatePublishingIdentity(
2225
2753
  { ...globalPackage, ...target.package },
2226
2754
  registry,
@@ -2333,16 +2861,28 @@ function mapFernConfigToSdkConfigV1(input) {
2333
2861
  package: outputPackage,
2334
2862
  generation: outputGeneration
2335
2863
  }, index) => {
2864
+ const distribution = applyCliDistribution(output, invocation.cliDistribution, index);
2865
+ invocation.unsupportedFields.push(...distribution.diagnostics);
2336
2866
  const target = {
2337
2867
  language,
2338
- output,
2868
+ output: distribution.output,
2339
2869
  ...optional("sdkName", generator.sdkName),
2340
2870
  ...optional("sdkVersion", generator.sdkVersion),
2341
2871
  ...optional("client", clients.targetOverrides[index]),
2342
2872
  ...optional("docs", docs.targetOverrides[index])
2343
2873
  };
2344
- if (Object.keys(invocation.package).length > 0 || outputPackage || generator.package) {
2345
- target.package = { ...invocation.package, ...outputPackage, ...generator.package };
2874
+ const targetPackage = { ...invocation.package, ...outputPackage, ...generator.package };
2875
+ invocation.unsupportedFields.push(
2876
+ ...resolveTargetPackage(
2877
+ targetPackage,
2878
+ invocation.packageFallbacks,
2879
+ generator,
2880
+ output.delivery,
2881
+ index
2882
+ )
2883
+ );
2884
+ if (Object.keys(targetPackage).length > 0 || outputPackage || generator.package) {
2885
+ target.package = targetPackage;
2346
2886
  }
2347
2887
  const targetGeneration = {
2348
2888
  ...generations.targetOverrides[index],
@@ -2524,10 +3064,14 @@ function mapInvocation(generator, language, activeLanguages, index) {
2524
3064
  ...optional("smartCasingDigitWordBoundary", smartCasingDigitWordBoundary)
2525
3065
  };
2526
3066
  }
2527
- const targetGeneration = mapLanguageConfig(language, config, state, packageConfig, [
2528
- ...prefix,
2529
- "config"
2530
- ]);
3067
+ const targetGeneration = mapLanguageConfig(
3068
+ language,
3069
+ config,
3070
+ state,
3071
+ packageConfig,
3072
+ [...prefix, "config"],
3073
+ index
3074
+ );
2531
3075
  mapPublishMetadata(generator.publishMetadata, packageConfig);
2532
3076
  if (generator.keywords?.length) {
2533
3077
  generation.reservedKeywords = [...generator.keywords];
@@ -2544,12 +3088,22 @@ function mapInvocation(generator, language, activeLanguages, index) {
2544
3088
  }
2545
3089
  const docs = mapReadme(generator.readme, language, activeLanguages, state, [...prefix, "readme"]);
2546
3090
  const unsupportedFields = [
3091
+ ...state.valueDiagnostics ?? [],
2547
3092
  ...collectUnsupported(config, [...prefix, "config"], state),
2548
3093
  ...settings ? collectUnsupported(settings, [...prefix, "settings"], state) : [],
2549
3094
  ...isObject(generator.readme) ? collectUnsupported(generator.readme, [...prefix, "readme"], state) : [],
2550
3095
  ...unsupportedResolvedFields(generator, index, apiWideIdempotency !== void 0)
2551
3096
  ];
2552
- return { client, docs, generation, package: packageConfig, targetGeneration, unsupportedFields };
3097
+ return {
3098
+ client,
3099
+ docs,
3100
+ generation,
3101
+ package: packageConfig,
3102
+ packageFallbacks: state.packageFallbacks ?? [],
3103
+ ...optional("cliDistribution", state.cliDistribution),
3104
+ targetGeneration,
3105
+ unsupportedFields
3106
+ };
2553
3107
  }
2554
3108
  function unsupportedResolvedFields(generator, index, idempotencyMapped) {
2555
3109
  const values = [
@@ -2739,6 +3293,7 @@ function mapCommonConfig(language, config, state, client, generation, packageCon
2739
3293
  const packagePath = takeString(config, ["packagePath"], state, basePath);
2740
3294
  if (packagePath !== void 0) generation.layout = { packagePath };
2741
3295
  mapDependencies(config, state, packageConfig, basePath);
3296
+ mapPeerDependenciesMeta(config, state, packageConfig, basePath);
2742
3297
  }
2743
3298
  function mapTypescriptStreams(config, state, basePath) {
2744
3299
  const responseType = takeEnum(config, ["streamType"], ["wrapper", "web"], state, basePath);
@@ -2752,7 +3307,7 @@ function mapTypescriptStreams(config, state, basePath) {
2752
3307
  if (responseType === void 0 && fileResponseType === void 0) return void 0;
2753
3308
  return { enabled: true, ...compact({ responseType, fileResponseType }) };
2754
3309
  }
2755
- function mapLanguageConfig(language, config, state, packageConfig, basePath) {
3310
+ function mapLanguageConfig(language, config, state, packageConfig, basePath, targetIndex) {
2756
3311
  switch (language) {
2757
3312
  case "typescript":
2758
3313
  return mapTypescript(config, state, packageConfig, basePath);
@@ -2779,7 +3334,7 @@ function mapLanguageConfig(language, config, state, packageConfig, basePath) {
2779
3334
  case "swift":
2780
3335
  return mapSwift(config, state, basePath);
2781
3336
  case "cli":
2782
- return mapCli(config, state, packageConfig, basePath);
3337
+ return mapCli(config, state, packageConfig, basePath, targetIndex);
2783
3338
  case "terraform":
2784
3339
  return mapTerraform(config, state, packageConfig, basePath);
2785
3340
  }
@@ -2796,6 +3351,7 @@ function mapTypescript(config, state, packageConfig, basePath) {
2796
3351
  const packageManagerAtIndex = packageJsonPackageManager ? packageJsonPackageManager.indexOf("@", packageJsonPackageManager.startsWith("@") ? 1 : 0) : -1;
2797
3352
  const packageManagerVersionPart = packageManagerAtIndex >= 0 ? packageJsonPackageManager?.slice(packageManagerAtIndex + 1).split("+")[0] : void 0;
2798
3353
  const packageManagerVersion = packageManagerVersionPart || void 0;
3354
+ if (packageJsonManifest) liftPackageJsonDependencies(packageJsonManifest, state, packageConfig);
2799
3355
  const serdeLayer = takeBoolean(config, ["serdeLayer"], state, basePath);
2800
3356
  const noSerdeLayer = takeBoolean(config, ["noSerdeLayer"], state, basePath);
2801
3357
  const fetchSupport = takeEnum(config, ["fetchSupport"], ["native"], state, basePath);
@@ -2965,6 +3521,7 @@ function mapRust(config, state, packageConfig, basePath) {
2965
3521
  const description = takeString(config, ["packageDescription"], state, basePath);
2966
3522
  const documentationUrl = takeString(config, ["packageDocumentation"], state, basePath);
2967
3523
  Object.assign(packageConfig, compact({ packageName, repository, description, documentationUrl }));
3524
+ mapLicenseFallback(config, ["packageLicense"], state, basePath);
2968
3525
  return compact({
2969
3526
  dateTimeType: takeEnum(config, ["dateTimeType"], ["offset", "utc"], state, basePath),
2970
3527
  capitalizeInitialisms: takeBoolean(config, ["capitalizeInitialisms"], state, basePath),
@@ -2978,7 +3535,7 @@ function mapSwift(config, state, basePath) {
2978
3535
  nullableAsOptional: takeBoolean(config, ["nullableAsOptional"], state, basePath)
2979
3536
  });
2980
3537
  }
2981
- function mapCli(config, state, packageConfig, basePath) {
3538
+ function mapCli(config, state, packageConfig, basePath, targetIndex) {
2982
3539
  const identity = takeObject(config, ["packageIdentity"], state, basePath);
2983
3540
  if (identity) {
2984
3541
  Object.assign(
@@ -2989,10 +3546,13 @@ function mapCli(config, state, packageConfig, basePath) {
2989
3546
  description: takeString(identity.value, ["description"], state, identity.path)
2990
3547
  })
2991
3548
  );
3549
+ mapCliPackageIdentity(identity, state, targetIndex);
2992
3550
  }
3551
+ mapCliDistribution(config, state, basePath, targetIndex);
2993
3552
  return compact({
2994
3553
  paginationParameters: takeStringArray(config, ["paginationParameters"], state, basePath),
2995
- skills: takeBoolean(config, ["skills"], state, basePath)
3554
+ skills: takeBoolean(config, ["skills"], state, basePath),
3555
+ ...mapCliGeneration(config, state, basePath, targetIndex)
2996
3556
  });
2997
3557
  }
2998
3558
  function mapTerraform(config, state, packageConfig, basePath) {
@@ -3076,6 +3636,48 @@ function mapDependencies(config, state, packageConfig, basePath) {
3076
3636
  if (dependencies.length) packageConfig[field] = dependencies;
3077
3637
  }
3078
3638
  }
3639
+ function liftPackageJsonDependencies(manifest, state, packageConfig) {
3640
+ for (const [field, target] of [
3641
+ ["dependencies", "extraDependencies"],
3642
+ ["devDependencies", "extraDevDependencies"],
3643
+ ["peerDependencies", "extraPeerDependencies"]
3644
+ ]) {
3645
+ const located = takeObject(manifest.value, [field], state, manifest.path);
3646
+ if (!located) continue;
3647
+ const lifted = Object.entries(located.value).flatMap(
3648
+ ([name, dependency]) => mapDependency(name, dependency, state, [...located.path, name])
3649
+ );
3650
+ if (lifted.length) packageConfig[target] = overrideByName(packageConfig[target] ?? [], lifted);
3651
+ }
3652
+ const meta = takeObject(manifest.value, ["peerDependenciesMeta"], state, manifest.path);
3653
+ if (!meta) return;
3654
+ const entries = peerDependenciesMetaEntries(meta, state);
3655
+ if (entries.length) {
3656
+ packageConfig.extraPeerDependenciesMeta = {
3657
+ ...packageConfig.extraPeerDependenciesMeta,
3658
+ ...Object.fromEntries(entries)
3659
+ };
3660
+ }
3661
+ }
3662
+ function overrideByName(existing, overrides) {
3663
+ const byName = new Map(existing.map((dependency) => [dependency.name, dependency]));
3664
+ overrides.forEach((dependency) => byName.set(dependency.name, dependency));
3665
+ return [...byName.values()];
3666
+ }
3667
+ function mapPeerDependenciesMeta(config, state, packageConfig, basePath) {
3668
+ const located = takeObject(config, ["extraPeerDependenciesMeta"], state, basePath);
3669
+ if (!located) return;
3670
+ const entries = peerDependenciesMetaEntries(located, state);
3671
+ if (entries.length) packageConfig.extraPeerDependenciesMeta = Object.fromEntries(entries);
3672
+ }
3673
+ function peerDependenciesMetaEntries(located, state) {
3674
+ return Object.entries(located.value).flatMap(([name, value]) => {
3675
+ const parsed = jsonObjectSchema.safeParse(value);
3676
+ if (!parsed.success) return [];
3677
+ consume(state, [...located.path, name]);
3678
+ return [[name, parsed.data]];
3679
+ });
3680
+ }
3079
3681
  function mapDependency(name, value, state, path) {
3080
3682
  if (typeof value === "string" && value) {
3081
3683
  consume(state, path);
@@ -3083,13 +3685,7 @@ function mapDependency(name, value, state, path) {
3083
3685
  }
3084
3686
  if (!isObject(value)) return [];
3085
3687
  const version = takeString(value, ["version"], state, path);
3086
- const gitUrl = takeString(value, ["git"], state, path);
3087
- const localPath = takeString(value, ["path"], state, path);
3088
- const source = gitUrl ? {
3089
- type: "git",
3090
- url: gitUrl,
3091
- ...optional("ref", takeString(value, ["ref"], state, path))
3092
- } : localPath ? { type: "path", path: localPath } : void 0;
3688
+ const source = takeDependencySource(value, state, path);
3093
3689
  if (!version && !source) return [];
3094
3690
  return [
3095
3691
  {
@@ -3105,6 +3701,17 @@ function mapDependency(name, value, state, path) {
3105
3701
  }
3106
3702
  ];
3107
3703
  }
3704
+ function takeDependencySource(value, state, path) {
3705
+ const gitUrl = takeString(value, ["git"], state, path);
3706
+ if (gitUrl) {
3707
+ const ref = takeString(value, ["ref", "rev", "branch"], state, path);
3708
+ return { type: "git", url: gitUrl, ...optional("ref", ref) };
3709
+ }
3710
+ const localPath = takeString(value, ["path"], state, path);
3711
+ if (localPath) return { type: "path", path: localPath };
3712
+ const registry = takeString(value, ["registry"], state, path);
3713
+ return registry ? { type: "registry", registry } : void 0;
3714
+ }
3108
3715
  function takeCustomSections(value, language, activeLanguages, state, basePath) {
3109
3716
  const located = locate(value, ["customSections"], basePath);
3110
3717
  if (!located || !Array.isArray(located.value)) return void 0;
@@ -3293,6 +3900,15 @@ var compatibilityConfigSchema = z.strictObject({
3293
3900
  legacyInput: legacyInputSchema.optional().describe("Original producer input retained only for migration debugging."),
3294
3901
  unsupportedFields: z.array(unsupportedFieldSchema).optional().describe("Legacy fields that could not be represented in normalized configuration.")
3295
3902
  });
3903
+
3904
+ // src/sdk-config-ir/v1/diagnostics.ts
3905
+ function getSdkConfigIrV1Diagnostics(config) {
3906
+ if (config.target.language !== "cli") {
3907
+ return [];
3908
+ }
3909
+ const diagnostic = cliDistributionModeDiagnostic(config.output, ["output"]);
3910
+ return diagnostic ? [diagnostic] : [];
3911
+ }
3296
3912
  var generationAssetSchema = z.discriminatedUnion("type", [
3297
3913
  z.strictObject({
3298
3914
  type: z.literal("path").describe("Loads the generation asset from a relative path."),
@@ -3595,6 +4211,12 @@ var githubOutputSchema2 = z.strictObject({
3595
4211
  /** Automatically merge the GitHub publishing PR. Defaults to false. */
3596
4212
  autoMerge: z.boolean().default(false).describe("Whether the GitHub publisher automatically merges its pull request.")
3597
4213
  });
4214
+ var distributionShape = {
4215
+ /** Valid only for `cli` targets with GitHub delivery; secret names only, never values. */
4216
+ distribution: cliDistributionConfigSchema.optional().describe(
4217
+ "CLI release channels published by the generated release workflow; credentials are GitHub Actions secret names."
4218
+ )
4219
+ };
3598
4220
  var credentialResolutionShape = {
3599
4221
  /** Where output credential references are resolved. */
3600
4222
  credentialResolution: credentialResolutionSchema.default("refs-only").describe("Where output credential references are resolved.")
@@ -3603,18 +4225,21 @@ var filesOutputSchema2 = z.strictObject({
3603
4225
  delivery: z.literal("files").describe("Writes the generated SDK to a directory."),
3604
4226
  path: nonEmptyStringSchema.optional().describe("The destination directory for generated files."),
3605
4227
  publish: publishConfigSchema.optional().describe("Optional package registry publication."),
4228
+ ...distributionShape,
3606
4229
  ...credentialResolutionShape
3607
4230
  });
3608
4231
  var zipOutputSchema2 = z.strictObject({
3609
4232
  delivery: z.literal("zip").describe("Packages the generated SDK as a ZIP archive."),
3610
4233
  fileName: nonEmptyStringSchema.optional().describe("The generated ZIP archive file name."),
3611
4234
  publish: publishConfigSchema.optional().describe("Optional package registry publication."),
4235
+ ...distributionShape,
3612
4236
  ...credentialResolutionShape
3613
4237
  });
3614
4238
  var githubDeliverySchema2 = z.strictObject({
3615
4239
  delivery: z.literal("github").describe("Delivers the generated SDK to GitHub."),
3616
4240
  github: githubOutputSchema2.describe("GitHub repository delivery settings."),
3617
4241
  publish: publishConfigSchema.optional().describe("Optional package registry publication."),
4242
+ ...distributionShape,
3618
4243
  ...credentialResolutionShape
3619
4244
  });
3620
4245
  var outputConfigSchema = z.discriminatedUnion(
@@ -3699,7 +4324,7 @@ var targetConfigSchema = z.strictObject({
3699
4324
  // src/sdk-config-ir/v1/sdk-config-ir-v1.ts
3700
4325
  var SDK_CONFIG_IR_V1_SCHEMA_VERSION = "sdk-config-ir/v1";
3701
4326
  var publishRegistryLanguages2 = {
3702
- npm: ["typescript", "mcp"],
4327
+ npm: ["typescript", "mcp", "cli"],
3703
4328
  pypi: ["python"],
3704
4329
  maven: ["java", "kotlin"],
3705
4330
  nuget: ["csharp"],
@@ -3754,6 +4379,28 @@ function validatePublishingIdentity2(packageConfig, registry, context) {
3754
4379
  });
3755
4380
  }
3756
4381
  }
4382
+ function validateCliOutput(output, language, context) {
4383
+ if (output.distribution !== void 0 && language !== "cli") {
4384
+ context.addIssue({
4385
+ code: "custom",
4386
+ message: `output.distribution is supported only for target language "cli", not "${language}"`,
4387
+ path: ["output", "distribution"]
4388
+ });
4389
+ } else if (output.distribution !== void 0 && output.delivery !== "github") {
4390
+ context.addIssue({
4391
+ code: "custom",
4392
+ message: CLI_DISTRIBUTION_REQUIRES_GITHUB_DELIVERY_MESSAGE,
4393
+ path: ["output", "distribution"]
4394
+ });
4395
+ }
4396
+ if (language === "cli" && output.publish && output.delivery !== "github") {
4397
+ context.addIssue({
4398
+ code: "custom",
4399
+ message: CLI_PUBLISH_REQUIRES_GITHUB_DELIVERY_MESSAGE,
4400
+ path: ["output", "publish"]
4401
+ });
4402
+ }
4403
+ }
3757
4404
  var sdkConfigIrV1Schema = z.strictObject({
3758
4405
  schemaVersion: z.literal(SDK_CONFIG_IR_V1_SCHEMA_VERSION).describe("The version of the normalized SDK Config IR schema."),
3759
4406
  source: sourceConfigSchema.describe("Materialized API descriptions consumed by generation."),
@@ -3815,6 +4462,7 @@ var sdkConfigIrV1Schema = z.strictObject({
3815
4462
  });
3816
4463
  }
3817
4464
  }
4465
+ validateCliOutput(output, target.language, context);
3818
4466
  if (compatibility?.legacyInput) {
3819
4467
  const expectedKind = target.sourceOrigin === "postman" ? "postman-build-parameters" : "fern-generator-invocation";
3820
4468
  if (compatibility.legacyInput.kind !== expectedKind) {
@@ -3854,6 +4502,6 @@ function parseSdkConfigIrV1ForConsumption(value) {
3854
4502
  return parseSdkConfigIrV1(stripUnknownProperties(value, sdkConfigIrV1InputJsonSchema));
3855
4503
  }
3856
4504
 
3857
- export { FernConfigMappingError, SDK_CONFIG_IR_V1_SCHEMA_VERSION, SDK_CONFIG_V1_SCHEMA_VERSION, apiConfigSchema, apiImportSettingsSchema, authConfigSchema, authSchemeSchema, cliGenerationConfigSchema, clientConfigOverrideSchema, clientConfigSchema, clientRetryOverrideSchema, compatibilityConfigSchema, composerPackageNameSchema, csharpGenerationConfigSchema, dependencySchema, docsConfigSchema, exactSemverSchema, generationConfigSchema, goGenerationConfigSchema, goModulePathSchema, javaGenerationConfigSchema, jsonObjectSchema, jsonValueSchema, kotlinGenerationConfigSchema, languageGenerationConfigSchema, legacyInputSchema, mapFernConfigToSdkConfigV1, mcpAvailabilityStatuses, mcpGenerationConfigSchema, nonEmptyStringSchema, outputConfigSchema, packageConfigSchema, parseSdkConfigIrV1, parseSdkConfigIrV1ForConsumption, parseSdkConfigV1, parseSdkConfigV1ForConsumption, phpGenerationConfigSchema, publishConfigSchema, publishRegistrySchema, pythonGenerationConfigSchema, readmeCustomSectionSchema, readmeEndpointSchema, relativePathSchema, replayConfigSchema, rubyGenerationConfigSchema, rustGenerationConfigSchema, sdkConfigIrCapabilitySchema, sdkConfigIrV1Schema, sdkConfigV1ApiConfigSchema, sdkConfigV1AuthConfigSchema, sdkConfigV1AuthSchemeSchema, cliGenerationConfigSchema as sdkConfigV1CliGenerationConfigSchema, sdkConfigV1ClientConfigOverrideSchema, sdkConfigV1ClientConfigSchema, composerPackageNameSchema as sdkConfigV1ComposerPackageNameSchema, csharpGenerationConfigSchema as sdkConfigV1CsharpGenerationConfigSchema, sdkConfigV1DependencySchema, sdkConfigV1DocsConfigSchema, exactSemverSchema as sdkConfigV1ExactSemverSchema, sdkConfigV1GenerationConfigOverrideSchema, sdkConfigV1GenerationConfigSchema, goGenerationConfigSchema as sdkConfigV1GoGenerationConfigSchema, goModulePathSchema as sdkConfigV1GoModulePathSchema, javaGenerationConfigSchema as sdkConfigV1JavaGenerationConfigSchema, kotlinGenerationConfigSchema as sdkConfigV1KotlinGenerationConfigSchema, mcpGenerationConfigSchema as sdkConfigV1McpGenerationConfigSchema, nonEmptyStringSchema as sdkConfigV1NonEmptyStringSchema, sdkConfigV1OutputConfigSchema, sdkConfigV1PackageConfigSchema, phpGenerationConfigSchema as sdkConfigV1PhpGenerationConfigSchema, sdkConfigV1PublishConfigSchema, sdkConfigV1PublishRegistrySchema, pythonGenerationConfigSchema as sdkConfigV1PythonGenerationConfigSchema, sdkConfigV1ReadmeCustomSectionSchema, sdkConfigV1ReadmeEndpointSchema, relativePathSchema as sdkConfigV1RelativePathSchema, sdkConfigV1ReplayConfigSchema, rubyGenerationConfigSchema as sdkConfigV1RubyGenerationConfigSchema, rustGenerationConfigSchema as sdkConfigV1RustGenerationConfigSchema, sdkConfigV1Schema, sdkConfigV1SourceConfigSchema, sdkConfigV1SourceSpecSchema, swiftGenerationConfigSchema as sdkConfigV1SwiftGenerationConfigSchema, sdkConfigV1TargetSchema, terraformGenerationConfigSchema as sdkConfigV1TerraformGenerationConfigSchema, typescriptGenerationConfigSchema as sdkConfigV1TypescriptGenerationConfigSchema, sdkConfigV1WebhookSignatureSchema, serializeSdkConfigIrV1, sourceConfigSchema, sourceSpecConfigSchema, sourceSpecTypeSchema, splitSdkConfigV1TargetGeneration, swiftGenerationConfigSchema, targetConfigSchema, targetLanguageSchema, terraformGenerationConfigSchema, typescriptGenerationConfigSchema, unsupportedFieldSchema, validateSdkConfigV1, webhookSignatureSchema };
4505
+ export { CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE, FernConfigMappingError, SDK_CONFIG_IR_V1_SCHEMA_VERSION, CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE as SDK_CONFIG_V1_CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE, SDK_CONFIG_V1_SCHEMA_VERSION, apiConfigSchema, apiImportSettingsSchema, authConfigSchema, authSchemeSchema, cliDistributionConfigSchema, cliDistributionGithubAppSchema, cliDistributionHomebrewSchema, cliDistributionScoopSchema, cliGenerationConfigSchema, clientConfigOverrideSchema, clientConfigSchema, clientRetryOverrideSchema, compatibilityConfigSchema, composerPackageNameSchema, csharpGenerationConfigSchema, dependencySchema, docsConfigSchema, exactSemverSchema, generationConfigSchema, getSdkConfigIrV1Diagnostics, getSdkConfigV1Diagnostics, githubActionsSecretNameSchema, goGenerationConfigSchema, goModulePathSchema, javaGenerationConfigSchema, jsonObjectSchema, jsonValueSchema, kotlinGenerationConfigSchema, languageGenerationConfigSchema, legacyInputSchema, mapFernConfigToSdkConfigV1, mcpAvailabilityStatuses, mcpGenerationConfigSchema, nonEmptyStringSchema, outputConfigSchema, packageConfigSchema, parseSdkConfigIrV1, parseSdkConfigIrV1ForConsumption, parseSdkConfigV1, parseSdkConfigV1ForConsumption, phpGenerationConfigSchema, publishConfigSchema, publishRegistrySchema, pythonGenerationConfigSchema, readmeCustomSectionSchema, readmeEndpointSchema, relativePathSchema, replayConfigSchema, rubyGenerationConfigSchema, rustGenerationConfigSchema, sdkConfigIrCapabilitySchema, sdkConfigIrV1Schema, sdkConfigV1ApiConfigSchema, sdkConfigV1AuthConfigSchema, sdkConfigV1AuthSchemeSchema, cliDistributionConfigSchema as sdkConfigV1CliDistributionConfigSchema, cliDistributionGithubAppSchema as sdkConfigV1CliDistributionGithubAppSchema, cliDistributionHomebrewSchema as sdkConfigV1CliDistributionHomebrewSchema, cliDistributionScoopSchema as sdkConfigV1CliDistributionScoopSchema, cliGenerationConfigSchema as sdkConfigV1CliGenerationConfigSchema, sdkConfigV1CliOutputConfigSchema, sdkConfigV1ClientConfigOverrideSchema, sdkConfigV1ClientConfigSchema, composerPackageNameSchema as sdkConfigV1ComposerPackageNameSchema, csharpGenerationConfigSchema as sdkConfigV1CsharpGenerationConfigSchema, sdkConfigV1DependencySchema, sdkConfigV1DocsConfigSchema, exactSemverSchema as sdkConfigV1ExactSemverSchema, sdkConfigV1GenerationConfigOverrideSchema, sdkConfigV1GenerationConfigSchema, githubActionsSecretNameSchema as sdkConfigV1GithubActionsSecretNameSchema, goGenerationConfigSchema as sdkConfigV1GoGenerationConfigSchema, goModulePathSchema as sdkConfigV1GoModulePathSchema, javaGenerationConfigSchema as sdkConfigV1JavaGenerationConfigSchema, kotlinGenerationConfigSchema as sdkConfigV1KotlinGenerationConfigSchema, mcpGenerationConfigSchema as sdkConfigV1McpGenerationConfigSchema, nonEmptyStringSchema as sdkConfigV1NonEmptyStringSchema, sdkConfigV1OutputConfigSchema, sdkConfigV1PackageConfigSchema, phpGenerationConfigSchema as sdkConfigV1PhpGenerationConfigSchema, sdkConfigV1PublishConfigSchema, sdkConfigV1PublishRegistrySchema, pythonGenerationConfigSchema as sdkConfigV1PythonGenerationConfigSchema, sdkConfigV1ReadmeCustomSectionSchema, sdkConfigV1ReadmeEndpointSchema, relativePathSchema as sdkConfigV1RelativePathSchema, sdkConfigV1ReplayConfigSchema, rubyGenerationConfigSchema as sdkConfigV1RubyGenerationConfigSchema, rustGenerationConfigSchema as sdkConfigV1RustGenerationConfigSchema, sdkConfigV1Schema, sdkConfigV1SourceConfigSchema, sdkConfigV1SourceSpecSchema, swiftGenerationConfigSchema as sdkConfigV1SwiftGenerationConfigSchema, sdkConfigV1TargetSchema, terraformGenerationConfigSchema as sdkConfigV1TerraformGenerationConfigSchema, typescriptGenerationConfigSchema as sdkConfigV1TypescriptGenerationConfigSchema, sdkConfigV1WebhookSignatureSchema, serializeSdkConfigIrV1, sourceConfigSchema, sourceSpecConfigSchema, sourceSpecTypeSchema, splitSdkConfigV1TargetGeneration, swiftGenerationConfigSchema, targetConfigSchema, targetLanguageSchema, terraformGenerationConfigSchema, typescriptGenerationConfigSchema, unsupportedFieldSchema, validateSdkConfigV1, webhookSignatureSchema };
3858
4506
  //# sourceMappingURL=index.js.map
3859
4507
  //# sourceMappingURL=index.js.map