@postman/sdk-config 0.11.0 → 0.13.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 +18 -6
  2. package/dist/index.cjs +372 -24
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +372 -24
  7. package/dist/index.js.map +1 -1
  8. package/dist/sdk-config/index.cjs +370 -23
  9. package/dist/sdk-config/index.cjs.map +1 -1
  10. package/dist/sdk-config/index.d.cts +1 -1
  11. package/dist/sdk-config/index.d.ts +1 -1
  12. package/dist/sdk-config/index.js +370 -23
  13. package/dist/sdk-config/index.js.map +1 -1
  14. package/dist/sdk-config/v1/index.cjs +370 -23
  15. package/dist/sdk-config/v1/index.cjs.map +1 -1
  16. package/dist/sdk-config/v1/index.d.cts +632 -31
  17. package/dist/sdk-config/v1/index.d.ts +632 -31
  18. package/dist/sdk-config/v1/index.js +370 -23
  19. package/dist/sdk-config/v1/index.js.map +1 -1
  20. package/dist/sdk-config-ir/index.cjs +57 -6
  21. package/dist/sdk-config-ir/index.cjs.map +1 -1
  22. package/dist/sdk-config-ir/index.d.cts +1 -1
  23. package/dist/sdk-config-ir/index.d.ts +1 -1
  24. package/dist/sdk-config-ir/index.js +57 -6
  25. package/dist/sdk-config-ir/index.js.map +1 -1
  26. package/dist/sdk-config-ir/v1/index.cjs +57 -6
  27. package/dist/sdk-config-ir/v1/index.cjs.map +1 -1
  28. package/dist/sdk-config-ir/v1/index.d.cts +211 -4
  29. package/dist/sdk-config-ir/v1/index.d.ts +211 -4
  30. package/dist/sdk-config-ir/v1/index.js +57 -6
  31. package/dist/sdk-config-ir/v1/index.js.map +1 -1
  32. package/dist/{typescript-CsAa1VSy.d.cts → typescript-BFRxjAah.d.cts} +65 -0
  33. package/dist/{typescript-CsAa1VSy.d.ts → typescript-BFRxjAah.d.ts} +65 -0
  34. package/package.json +1 -1
  35. package/src/sdk-config/v1/README.md +32 -11
  36. package/src/sdk-config-ir/v1/README.md +115 -60
package/dist/index.js CHANGED
@@ -432,6 +432,12 @@ var dependencySchema = z.strictObject({
432
432
  }).refine(({ source, version }) => source !== void 0 || version !== void 0, {
433
433
  message: "dependency must define a version or a source"
434
434
  });
435
+ var bugsSchema = z.strictObject({
436
+ url: nonEmptyStringSchema.optional().describe("The issue tracker URL."),
437
+ email: nonEmptyStringSchema.optional().describe("The address for reporting issues.")
438
+ }).refine(({ url, email }) => url !== void 0 || email !== void 0, {
439
+ message: "bugs must define a url or an email"
440
+ });
435
441
  var projectUrlSchema = z.strictObject({
436
442
  label: nonEmptyStringSchema.describe("The label displayed for the project link."),
437
443
  url: nonEmptyStringSchema.describe("The project link URL.")
@@ -446,6 +452,7 @@ var packageConfigSchema = z.strictObject({
446
452
  description: z.string().optional().describe("The package description used in generated metadata."),
447
453
  repository: nonEmptyStringSchema.optional().describe("The source repository for the generated SDK."),
448
454
  homepage: nonEmptyStringSchema.optional().describe("The package homepage URL."),
455
+ bugs: bugsSchema.optional().describe("Where issues with the package are reported."),
449
456
  documentationUrl: nonEmptyStringSchema.optional().describe("The package documentation URL."),
450
457
  authors: z.array(authorSchema).optional().describe("Authors credited in package metadata."),
451
458
  developers: z.array(developerSchema).optional().describe("Developers credited in package metadata."),
@@ -501,7 +508,10 @@ var cliDistributionConfigSchema = z.strictObject({
501
508
  "A GitHub App used to mint short-lived tokens for every channel without its own tokenSecretName."
502
509
  ),
503
510
  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.")
511
+ scoop: cliDistributionScoopSchema.optional().describe("Publishes a Scoop manifest to a bucket repository on each tagged release."),
512
+ githubAttestations: z.union([z.literal("auto"), z.boolean()], { error: 'must be "auto", true, or false' }).optional().describe(
513
+ 'Whether the generated release workflow attests build provenance. "auto" (the generator default) attests only on public repositories; true always attests, for private repositories on GitHub Enterprise Cloud; false omits the attestation step and its permissions.'
514
+ )
505
515
  }).superRefine((distribution, context) => {
506
516
  if (distribution.homebrew !== void 0 || distribution.scoop !== void 0) {
507
517
  return;
@@ -514,6 +524,9 @@ var cliDistributionConfigSchema = z.strictObject({
514
524
  });
515
525
  return;
516
526
  }
527
+ if (distribution.githubAttestations !== void 0) {
528
+ return;
529
+ }
517
530
  context.addIssue({
518
531
  code: "custom",
519
532
  message: "distribution must configure at least one channel: homebrew or scoop"
@@ -525,14 +538,17 @@ var CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE = "CLI_DISTRIBUTION_NON_RELEASE_MODE"
525
538
  var nonReleaseGithubModes = /* @__PURE__ */ new Set(["pull-request", "push", "commit"]);
526
539
  function cliDistributionModeDiagnostic(output, outputPath) {
527
540
  const mode = output?.github?.mode;
528
- if (output?.distribution === void 0 || mode === void 0 || !nonReleaseGithubModes.has(mode)) {
541
+ const distribution = output?.distribution;
542
+ if (distribution === void 0 || mode === void 0 || !nonReleaseGithubModes.has(mode)) {
529
543
  return void 0;
530
544
  }
545
+ const hasChannel = distribution.homebrew !== void 0 || distribution.scoop !== void 0;
546
+ const consequence = hasChannel ? "Homebrew and Scoop publish only after a semantic-version tag is pushed to the repository." : "the release workflow, including its githubAttestations setting, runs only after a semantic-version tag is pushed to the repository.";
531
547
  return {
532
548
  code: CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE,
533
549
  severity: "warning",
534
550
  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.`,
551
+ reason: `GitHub mode "${mode}" does not create a release tag, so ${consequence}`,
536
552
  suggestedAction: 'Use mode "release" or "commit-and-release", or push a version tag after merging generated changes.'
537
553
  };
538
554
  }
@@ -967,6 +983,7 @@ var DISTRIBUTION_KEYS = {
967
983
  homebrew: { tap: "tap", formula: "formula", tokenEnvironmentVariable: "tokenSecretName" },
968
984
  scoop: { bucket: "bucket", tokenEnvironmentVariable: "tokenSecretName" }
969
985
  };
986
+ var DISTRIBUTION_SCALAR_KEYS = ["githubAttestations"];
970
987
  function mapCliDistribution(config, state, basePath, targetIndex) {
971
988
  if (!Object.hasOwn(config, "distribution")) return;
972
989
  const raw = config.distribution;
@@ -1013,6 +1030,11 @@ function translateDistribution(distribution, path) {
1013
1030
  }
1014
1031
  value[block] = translatedBlock;
1015
1032
  }
1033
+ for (const key of DISTRIBUTION_SCALAR_KEYS) {
1034
+ if (!Object.hasOwn(distribution, key)) continue;
1035
+ value[key] = distribution[key];
1036
+ consumedPaths.push([...path, key]);
1037
+ }
1016
1038
  return { value, consumedPaths, legacyKeys };
1017
1039
  }
1018
1040
  function invalidDistributionDiagnostic(issue, path, legacyKeys, targetIndex) {
@@ -1152,6 +1174,7 @@ function mapOutput(generator, language, index) {
1152
1174
  output.publishV2,
1153
1175
  rawOutput,
1154
1176
  language,
1177
+ "zip",
1155
1178
  [...outputPath, "publishV2"],
1156
1179
  state,
1157
1180
  index
@@ -1173,6 +1196,7 @@ function mapOutput(generator, language, index) {
1173
1196
  output.registryOverrides,
1174
1197
  rawOutput,
1175
1198
  language,
1199
+ "zip",
1176
1200
  [...outputPath, "registryOverrides"],
1177
1201
  state,
1178
1202
  index
@@ -1200,7 +1224,14 @@ function mapOutput(generator, language, index) {
1200
1224
  }
1201
1225
  }
1202
1226
  function sanitizeNormalizedOutput(output, rawOutput, state, index) {
1203
- const publish = output.publish ? sanitizePublishConfig(output.publish, output.publish, rawOutput, state, index) : void 0;
1227
+ const publish = output.publish ? sanitizePublishConfig(
1228
+ output.publish,
1229
+ output.publish,
1230
+ rawOutput,
1231
+ output.delivery,
1232
+ state,
1233
+ index
1234
+ ) : void 0;
1204
1235
  const publishShape = publish ? { publish } : {};
1205
1236
  if (output.delivery !== "github") return { ...output, ...publishShape };
1206
1237
  const { credentials, ...github } = output.github;
@@ -1227,12 +1258,21 @@ function githubOutput(value, publishInfo, rawOutput, language, sourcePath, state
1227
1258
  );
1228
1259
  consume(state, [...sourcePath, "owner"]);
1229
1260
  consume(state, [...sourcePath, "repo"]);
1230
- const publication = publishInfo ? mapPublication(publishInfo, rawOutput, language, [...sourcePath, "publishInfo"], state, index) : void 0;
1261
+ const publication = publishInfo ? mapPublication(
1262
+ publishInfo,
1263
+ rawOutput,
1264
+ language,
1265
+ "github",
1266
+ [...sourcePath, "publishInfo"],
1267
+ state,
1268
+ index
1269
+ ) : void 0;
1231
1270
  const type = stringValue(value.type);
1232
1271
  const licensePackage = mapGithubLicense(value.license, [...sourcePath, "license"], state);
1233
1272
  const reviewers = mapReviewers(value.reviewers, [...sourcePath, "reviewers"], state);
1234
1273
  if (type) consume(state, [...sourcePath, "type"]);
1235
1274
  if (typeof value.makePr === "boolean") consume(state, [...sourcePath, "makePr"]);
1275
+ if (value.workflows === true) consume(state, [...sourcePath, "workflows"]);
1236
1276
  if (stringValue(value.host)) consume(state, [...sourcePath, "host"]);
1237
1277
  if (stringValue(value.branch)) consume(state, [...sourcePath, "branch"]);
1238
1278
  consumeCredentialObject(value.credentials, [...sourcePath, "credentials"], state);
@@ -1319,7 +1359,7 @@ function reviewerNames(value, expectedType, sourcePath, state) {
1319
1359
  });
1320
1360
  return names.length ? names : void 0;
1321
1361
  }
1322
- function mapPublication(value, rawOutput, language, sourcePath, state, index) {
1362
+ function mapPublication(value, rawOutput, language, delivery, sourcePath, state, index) {
1323
1363
  if (!isObject(value))
1324
1364
  fail(
1325
1365
  "FERN_PUBLICATION_INVALID",
@@ -1340,6 +1380,7 @@ function mapPublication(value, rawOutput, language, sourcePath, state, index) {
1340
1380
  compact({ registry, url: registryUrl }),
1341
1381
  nested,
1342
1382
  rawOutput,
1383
+ delivery,
1343
1384
  state,
1344
1385
  index
1345
1386
  );
@@ -1360,7 +1401,7 @@ function mapPublication(value, rawOutput, language, sourcePath, state, index) {
1360
1401
  ...packageName || Object.keys(metadata).length ? { package: { ...optional("packageName", packageName), ...metadata } } : {}
1361
1402
  };
1362
1403
  }
1363
- function sanitizePublishConfig(publish, resolvedPublication, rawOutput, state, index) {
1404
+ function sanitizePublishConfig(publish, resolvedPublication, rawOutput, delivery, state, index) {
1364
1405
  const rawPublication = rawOutput?.location === publish.registry ? rawOutput : void 0;
1365
1406
  const rawPath = ["group", "generators", index, "output"];
1366
1407
  if (publish.registry === "npm" || publish.registry === "crates") {
@@ -1456,6 +1497,33 @@ function sanitizePublishConfig(publish, resolvedPublication, rawOutput, state, i
1456
1497
  signature
1457
1498
  });
1458
1499
  }
1500
+ if (publish.registry === "nuget" || publish.registry === "rubygems") {
1501
+ const apiKey = mapApiKeyExpression(
1502
+ publish.registry,
1503
+ rawPublication,
1504
+ resolvedPublication,
1505
+ rawPath,
1506
+ state,
1507
+ index,
1508
+ fernDefaultApiKey(publish.registry, rawOutput, delivery)
1509
+ );
1510
+ warnForUnexpectedCredentialFields(
1511
+ rawPublication,
1512
+ resolvedPublication,
1513
+ ["apiKey"],
1514
+ rawPath,
1515
+ state,
1516
+ index
1517
+ );
1518
+ return compact({
1519
+ registry: publish.registry,
1520
+ url: publish.url,
1521
+ releaseBranch: publish.releaseBranch,
1522
+ tolerateRepublish: publish.tolerateRepublish,
1523
+ apiKey,
1524
+ trustedPublishing: usesTrustedPublishing(rawPublication) || void 0
1525
+ });
1526
+ }
1459
1527
  warnForUnrepresentableCredentials(rawPublication, resolvedPublication, rawPath, state, index);
1460
1528
  return compact({
1461
1529
  registry: publish.registry,
@@ -1464,11 +1532,19 @@ function sanitizePublishConfig(publish, resolvedPublication, rawOutput, state, i
1464
1532
  tolerateRepublish: publish.tolerateRepublish
1465
1533
  });
1466
1534
  }
1467
- function mapLegacyPublication(value, rawOutput, language, sourcePath, state, index) {
1535
+ function mapLegacyPublication(value, rawOutput, language, delivery, sourcePath, state, index) {
1468
1536
  const registry = defaultRegistry(language, sourcePath, index);
1469
1537
  const overrides = isObject(value) ? value : {};
1470
1538
  const override = isObject(overrides[registry]) ? overrides[registry] : {};
1471
- return mapPublication(override, rawOutput, language, [...sourcePath, registry], state, index);
1539
+ return mapPublication(
1540
+ override,
1541
+ rawOutput,
1542
+ language,
1543
+ delivery,
1544
+ [...sourcePath, registry],
1545
+ state,
1546
+ index
1547
+ );
1472
1548
  }
1473
1549
  function mapCredentialExpression(rawPublication, resolvedPublication, field, rawPath, state, index) {
1474
1550
  const rawValue = rawPublication?.[field];
@@ -1483,6 +1559,45 @@ function mapCredentialExpression(rawPublication, resolvedPublication, field, raw
1483
1559
  }
1484
1560
  return void 0;
1485
1561
  }
1562
+ var TRUSTED_PUBLISHING_VALUES = /* @__PURE__ */ new Set(["OIDC", "<USE_OIDC>"]);
1563
+ function rawApiKeyValue(raw) {
1564
+ return raw?.apiKey ?? raw?.["api-key"];
1565
+ }
1566
+ function usesTrustedPublishing(rawPublication) {
1567
+ const rawValue = rawApiKeyValue(rawPublication);
1568
+ return typeof rawValue === "string" && TRUSTED_PUBLISHING_VALUES.has(rawValue);
1569
+ }
1570
+ function mapApiKeyExpression(registry, rawPublication, resolvedPublication, rawPath, state, index, defaultSecretName) {
1571
+ const rawValue = rawApiKeyValue(rawPublication);
1572
+ if (isEnvironmentExpression(rawValue)) return rawValue;
1573
+ if (usesTrustedPublishing(rawPublication)) return void 0;
1574
+ if (defaultSecretName !== void 0) return defaultSecretName;
1575
+ if (rawValue === void 0 && !hasResolvedCredential(resolvedPublication, "apiKey")) {
1576
+ return void 0;
1577
+ }
1578
+ addCredentialDiagnostic(
1579
+ state,
1580
+ [...rawPath, "apiKey"],
1581
+ ["targets", index, "output", "publish", "apiKey"],
1582
+ apiKeyOmissionReason(registry)
1583
+ );
1584
+ return void 0;
1585
+ }
1586
+ function apiKeyOmissionReason(registry) {
1587
+ const label = registry === "nuget" ? "NuGet" : "RubyGems";
1588
+ return `Fern ${label} apiKey is not an exact environment expression naming a secret`;
1589
+ }
1590
+ var FERN_DEFAULT_NUGET_API_KEY = "${NUGET_API_TOKEN}";
1591
+ function fernDefaultApiKey(registry, rawOutput, delivery) {
1592
+ if (registry !== "nuget" || delivery !== "github" || rawOutput?.location !== "nuget") {
1593
+ return void 0;
1594
+ }
1595
+ const rawApiKey = rawApiKeyValue(rawOutput);
1596
+ if (typeof rawApiKey === "string" && TRUSTED_PUBLISHING_VALUES.has(rawApiKey)) {
1597
+ return void 0;
1598
+ }
1599
+ return FERN_DEFAULT_NUGET_API_KEY;
1600
+ }
1486
1601
  function mapPypiCredentialExpressions(rawPublication, resolvedPublication, rawPath, state, index) {
1487
1602
  const rawToken = rawPublication?.token;
1488
1603
  if (isEnvironmentExpression(rawToken)) {
@@ -2198,6 +2313,29 @@ var mavenSignatureSchema = z.strictObject({
2198
2313
  "The armored secret key, or an environment expression that resolves to it."
2199
2314
  )
2200
2315
  });
2316
+ function apiKeySecretSchema(registry, example) {
2317
+ return nonEmptyStringSchema.regex(
2318
+ /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/,
2319
+ `${registry} apiKey must be an environment expression naming a secret, such as ${example}`
2320
+ );
2321
+ }
2322
+ function apiKeySecretDescription(registry, example) {
2323
+ return `An environment expression naming the repository secret that the generated release workflow reads the ${registry} API key from, such as ${example}. This is a secret name, never the key itself.`;
2324
+ }
2325
+ function trustedPublishingSchema(registry) {
2326
+ return z.boolean().optional().describe(
2327
+ `Whether ${registry} publication uses trusted publishing (OIDC) instead of an API key secret.`
2328
+ );
2329
+ }
2330
+ function refineApiKeyAgainstTrustedPublishing(publish, context) {
2331
+ if (publish.trustedPublishing === true && publish.apiKey !== void 0) {
2332
+ context.addIssue({
2333
+ code: "custom",
2334
+ message: "apiKey must be omitted when trustedPublishing is true",
2335
+ path: ["apiKey"]
2336
+ });
2337
+ }
2338
+ }
2201
2339
  var commonPublishShape = {
2202
2340
  url: nonEmptyStringSchema.optional().describe("A custom package registry URL."),
2203
2341
  releaseBranch: nonEmptyStringSchema.optional().describe("The branch used by generated package release workflows."),
@@ -2233,12 +2371,16 @@ var sdkConfigV1PublishConfigSchema = z.discriminatedUnion("registry", [
2233
2371
  }),
2234
2372
  z.strictObject({
2235
2373
  registry: z.literal("nuget").describe("Publishes the package to a NuGet-compatible registry."),
2374
+ apiKey: apiKeySecretSchema("NuGet", "${NUGET_API_KEY}").optional().describe(apiKeySecretDescription("NuGet", "${NUGET_API_KEY}")),
2375
+ trustedPublishing: trustedPublishingSchema("NuGet"),
2236
2376
  ...commonPublishShape
2237
- }),
2377
+ }).superRefine(refineApiKeyAgainstTrustedPublishing),
2238
2378
  z.strictObject({
2239
2379
  registry: z.literal("rubygems").describe("Publishes the package to a RubyGems-compatible registry."),
2380
+ apiKey: apiKeySecretSchema("RubyGems", "${RUBYGEMS_API_KEY}").optional().describe(apiKeySecretDescription("RubyGems", "${RUBYGEMS_API_KEY}")),
2381
+ trustedPublishing: trustedPublishingSchema("RubyGems"),
2240
2382
  ...commonPublishShape
2241
- }),
2383
+ }).superRefine(refineApiKeyAgainstTrustedPublishing),
2242
2384
  z.strictObject({
2243
2385
  registry: z.literal("crates").describe("Publishes the crate to a Cargo-compatible registry."),
2244
2386
  token: nonEmptyStringSchema.optional().describe("The Cargo registry token, or an environment expression that resolves to it."),
@@ -2296,7 +2438,7 @@ var sdkConfigV1OutputConfigSchema = z.discriminatedUnion(
2296
2438
  );
2297
2439
  var cliDistributionShape = {
2298
2440
  distribution: cliDistributionConfigSchema.optional().describe(
2299
- "CLI release channels published by the generated release workflow; credentials are GitHub Actions secret names."
2441
+ "CLI release channels and build attestation settings for the generated release workflow; credentials are GitHub Actions secret names."
2300
2442
  )
2301
2443
  };
2302
2444
  function rejectDistributionWithoutGithub(output, context) {
@@ -2342,6 +2484,9 @@ var apiImportSettingsSchema = z.strictObject({
2342
2484
  discriminatedUnionV2: z.boolean().optional().describe("Whether oneOf and anyOf schemas use the newer discriminator inference rules."),
2343
2485
  undiscriminatedUnionsWithLiterals: z.boolean().optional().describe("Whether literal-bearing unions remain undiscriminated during import."),
2344
2486
  inlineAllOfSchemas: z.boolean().optional().describe("Whether schemas composed with allOf are inlined when possible."),
2487
+ inferDiscriminatedUnionBaseProperties: z.boolean().optional().describe(
2488
+ "Whether properties every discriminated-union variant shares are lifted onto the union."
2489
+ ),
2345
2490
  resolveSchemaCollisions: z.boolean().optional().describe("Whether colliding imported schema names are resolved automatically."),
2346
2491
  asyncApiMessageNaming: z.enum(["v1", "v2"]).optional().describe("The naming strategy used for messages imported from AsyncAPI."),
2347
2492
  defaultIntegerFormat: z.enum(["int32", "int64", "uint32", "uint64"]).optional().describe("The integer format used when a schema does not declare one.")
@@ -2440,7 +2585,7 @@ var packageScriptSchema = z.strictObject({
2440
2585
  command: nonEmptyStringSchema.describe("The command executed by the package script.")
2441
2586
  });
2442
2587
  var httpClientSchema = z.strictObject({
2443
- name: z.enum(["axios", "fetch"]).describe("The HTTP client used by generated TypeScript code.")
2588
+ name: z.enum(["axios", "fetch", "node-fetch"]).describe("The HTTP client used by generated TypeScript code.")
2444
2589
  });
2445
2590
  var typescriptGenerationConfigSchema = z.strictObject({
2446
2591
  typescriptVersion: nonEmptyStringSchema.optional().describe("The TypeScript version used by the generated project."),
@@ -2456,6 +2601,37 @@ var typescriptGenerationConfigSchema = z.strictObject({
2456
2601
  /** Name of the top-level namespace export emitted by Fern-compatible TypeScript SDKs. */
2457
2602
  namespaceExportName: nonEmptyStringSchema.optional().describe("The top-level namespace export name."),
2458
2603
  consolidateTypeFiles: z.boolean().optional().describe("Whether generated types are consolidated into fewer source files."),
2604
+ formDataSupport: z.enum(["Node16", "Node18"]).optional().describe(
2605
+ "The multipart form-data implementation: Node16 uses the form-data packages, Node18 uses the platform FormData."
2606
+ ),
2607
+ allowExtraFields: z.boolean().optional().describe(
2608
+ "Whether response parsing keeps unrecognized object keys and accepts unrecognized union members and enum values."
2609
+ ),
2610
+ generateSubpackageExports: z.boolean().optional().describe(
2611
+ "Whether the package manifest exports each subpackage client as its own entry point."
2612
+ ),
2613
+ exportAllRequestsAtRoot: z.boolean().optional().describe("Whether every request type is also exported from the package root."),
2614
+ generateEndpointMetadata: z.boolean().optional().describe("Whether generated clients expose metadata for each endpoint."),
2615
+ outputEsm: z.boolean().optional().describe(
2616
+ "Whether the package is marked as an ES module while still shipping its CommonJS build."
2617
+ ),
2618
+ neverThrowErrors: z.boolean().optional().describe(
2619
+ "Whether client methods return a wrapped response instead of throwing on a non-2xx status."
2620
+ ),
2621
+ includeOtherInUnionTypes: z.boolean().optional().describe("Whether union types gain a member for variants the SDK does not recognize."),
2622
+ includeUtilsOnUnionMembers: z.boolean().optional().describe("Whether helper utilities are generated on each member of a union type."),
2623
+ omitFernHeaders: z.boolean().optional().describe("Whether requests omit the SDK identification headers and the User-Agent header."),
2624
+ linter: z.enum(["biome", "oxlint", "none"]).optional().describe("The linter configured in the generated project."),
2625
+ formatter: z.enum(["prettier", "biome", "oxfmt", "none"]).optional().describe("The formatter configured in the generated project."),
2626
+ exports: jsonObjectSchema.optional().describe(
2627
+ "package.json exports entries, keyed by subpath, applied over the generated exports. Each subpath replaces the generated one."
2628
+ ),
2629
+ files: z.array(nonEmptyStringSchema).optional().describe("package.json files entries, listed ahead of the generated ones."),
2630
+ engines: z.record(nonEmptyStringSchema, nonEmptyStringSchema).optional().describe("package.json engines version ranges, keyed by engine name such as node."),
2631
+ lintStaged: z.record(nonEmptyStringSchema, z.array(nonEmptyStringSchema).min(1)).optional().describe("lint-staged commands to run, keyed by file glob."),
2632
+ browser: z.record(nonEmptyStringSchema, z.union([z.boolean(), nonEmptyStringSchema])).optional().describe(
2633
+ "package.json browser field: a module or file is excluded (false) or replaced by another path."
2634
+ ),
2459
2635
  allowCustomFetcher: z.boolean().optional().describe("Whether callers can supply a custom fetch implementation."),
2460
2636
  useBrandedStringAliases: z.boolean().optional().describe("Whether string aliases are emitted as distinct branded types."),
2461
2637
  useLegacyExports: z.boolean().optional().describe("Whether the package emits the legacy export layout."),
@@ -2529,7 +2705,7 @@ var pythonGenerationConfigSchema = z.strictObject({
2529
2705
  additionalInitExports: z.array(additionalInitExportSchema).optional().describe("Additional names re-exported from the package initializer.")
2530
2706
  });
2531
2707
  var rubyGenerationConfigSchema = z.strictObject({
2532
- requirePaths: z.array(nonEmptyStringSchema).optional().describe("Ruby load paths included in the generated gem specification.")
2708
+ requirePaths: z.array(nonEmptyStringSchema).optional().describe("Files the generated entry file loads with `require`.")
2533
2709
  });
2534
2710
  var rustGenerationConfigSchema = z.strictObject({
2535
2711
  /**
@@ -2871,16 +3047,21 @@ function mapFernConfigToSdkConfigV1(input) {
2871
3047
  ...optional("client", clients.targetOverrides[index]),
2872
3048
  ...optional("docs", docs.targetOverrides[index])
2873
3049
  };
2874
- const targetPackage = { ...invocation.package, ...outputPackage, ...generator.package };
3050
+ const mappedPackage = { ...invocation.package, ...outputPackage, ...generator.package };
2875
3051
  invocation.unsupportedFields.push(
2876
3052
  ...resolveTargetPackage(
2877
- targetPackage,
3053
+ mappedPackage,
2878
3054
  invocation.packageFallbacks,
2879
3055
  generator,
2880
3056
  output.delivery,
2881
3057
  index
2882
3058
  )
2883
3059
  );
3060
+ const targetPackage = withRustCratesPublishDefaults(mappedPackage, {
3061
+ language,
3062
+ output,
3063
+ generator
3064
+ });
2884
3065
  if (Object.keys(targetPackage).length > 0 || outputPackage || generator.package) {
2885
3066
  target.package = targetPackage;
2886
3067
  }
@@ -2949,6 +3130,21 @@ function mapFernConfigToSdkConfigV1(input) {
2949
3130
  ])
2950
3131
  };
2951
3132
  }
3133
+ function withRustCratesPublishDefaults(packageConfig, { language, output, generator }) {
3134
+ if (language !== "rust" || !("publish" in output) || output.publish?.registry !== "crates") {
3135
+ return packageConfig;
3136
+ }
3137
+ const { packageName } = packageConfig;
3138
+ return {
3139
+ ...packageName ? { description: `Rust SDK for ${packageName} generated by Fern` } : {},
3140
+ ...usesCustomLicenseFile(generator) ? {} : { license: { type: "MIT" } },
3141
+ ...packageConfig
3142
+ };
3143
+ }
3144
+ function usesCustomLicenseFile({ config, raw }) {
3145
+ const metadata = isObject(raw) && isObject(raw.metadata) ? raw.metadata : void 0;
3146
+ return isObject(config) && typeof config.packageLicenseFile === "string" || isObject(metadata?.license);
3147
+ }
2952
3148
  function resolveLanguage(generator, index) {
2953
3149
  const language = generator.sdkLanguage ?? generatorLanguages[generator.name];
2954
3150
  if (!language || !isFernLanguage(language)) {
@@ -3087,6 +3283,13 @@ function mapInvocation(generator, language, activeLanguages, index) {
3087
3283
  }
3088
3284
  }
3089
3285
  const docs = mapReadme(generator.readme, language, activeLanguages, state, [...prefix, "readme"]);
3286
+ if (language === "typescript") {
3287
+ const includeApiReference = takeBoolean(config, ["includeApiReference"], state, [
3288
+ ...prefix,
3289
+ "config"
3290
+ ]);
3291
+ if (includeApiReference !== void 0) docs.includeApiReference = includeApiReference;
3292
+ }
3090
3293
  const unsupportedFields = [
3091
3294
  ...state.valueDiagnostics ?? [],
3092
3295
  ...collectUnsupported(config, [...prefix, "config"], state),
@@ -3233,7 +3436,12 @@ function mapCommonConfig(language, config, state, client, generation, packageCon
3233
3436
  }
3234
3437
  const pythonClient = language === "python" ? takeObject(config, ["client"], state, basePath) : void 0;
3235
3438
  const typescriptNaming = language === "typescript" || language === "mcp" ? takeObject(config, ["naming"], state, basePath) : void 0;
3236
- const directClientName = takeString(config, ["clientName", "clientClassName"], state, basePath);
3439
+ const directClientName = takeString(
3440
+ config,
3441
+ language === "ruby" ? ["clientName"] : ["clientName", "clientClassName"],
3442
+ state,
3443
+ basePath
3444
+ );
3237
3445
  const nestedClientName = pythonClient ? takeString(pythonClient.value, ["className"], state, pythonClient.path) : typescriptNaming ? takeString(typescriptNaming.value, ["client"], state, typescriptNaming.path) : language === "ruby" ? (
3238
3446
  // Fern's Ruby generator renames the root client class (`Auth0::Management`) with it.
3239
3447
  takeString(config, ["clientModuleName"], state, basePath)
@@ -3267,7 +3475,13 @@ function mapCommonConfig(language, config, state, client, generation, packageCon
3267
3475
  });
3268
3476
  if (Object.keys(naming).length) generation.naming = naming;
3269
3477
  const wireTests = takeBoolean(config, ["enableWireTests", "generateWireTests"], state, basePath);
3270
- if (wireTests !== void 0) generation.wireTests = { enabled: wireTests };
3478
+ const wireTestsFallback = language === "typescript" ? takeBoolean(config, ["wireTestsFallbackToAutoGeneratedErrorExamples"], state, basePath) : void 0;
3479
+ if (wireTests !== void 0 || wireTestsFallback !== void 0) {
3480
+ generation.wireTests = { enabled: wireTests ?? true };
3481
+ if (wireTestsFallback !== void 0) {
3482
+ generation.wireTests.fallbackToGeneratedErrorExamples = wireTestsFallback;
3483
+ }
3484
+ }
3271
3485
  const webSockets = takeBoolean(
3272
3486
  config,
3273
3487
  ["generateWebSocketClients", "shouldGenerateWebsocketClients", "enableWebsockets"],
@@ -3310,10 +3524,10 @@ function mapTypescriptStreams(config, state, basePath) {
3310
3524
  function mapLanguageConfig(language, config, state, packageConfig, basePath, targetIndex) {
3311
3525
  switch (language) {
3312
3526
  case "typescript":
3313
- return mapTypescript(config, state, packageConfig, basePath);
3527
+ return mapTypescript(config, state, packageConfig, basePath, true);
3314
3528
  case "mcp":
3315
3529
  return {
3316
- ...mapTypescript(config, state, packageConfig, basePath),
3530
+ ...mapTypescript(config, state, packageConfig, basePath, false),
3317
3531
  ...mapMcp(config, state, basePath)
3318
3532
  };
3319
3533
  case "python":
@@ -3339,7 +3553,7 @@ function mapLanguageConfig(language, config, state, packageConfig, basePath, tar
3339
3553
  return mapTerraform(config, state, packageConfig, basePath);
3340
3554
  }
3341
3555
  }
3342
- function mapTypescript(config, state, packageConfig, basePath) {
3556
+ function mapTypescript(config, state, packageConfig, basePath, sdkTarget) {
3343
3557
  const packageJsonManifest = mapPackageManifest(
3344
3558
  config,
3345
3559
  ["packageJson"],
@@ -3352,18 +3566,74 @@ function mapTypescript(config, state, packageConfig, basePath) {
3352
3566
  const packageManagerVersionPart = packageManagerAtIndex >= 0 ? packageJsonPackageManager?.slice(packageManagerAtIndex + 1).split("+")[0] : void 0;
3353
3567
  const packageManagerVersion = packageManagerVersionPart || void 0;
3354
3568
  if (packageJsonManifest) liftPackageJsonDependencies(packageJsonManifest, state, packageConfig);
3569
+ const manifestOptions = sdkTarget && packageJsonManifest ? mapPackageManifestOptions(packageJsonManifest, state, packageConfig) : {};
3355
3570
  const serdeLayer = takeBoolean(config, ["serdeLayer"], state, basePath);
3356
3571
  const noSerdeLayer = takeBoolean(config, ["noSerdeLayer"], state, basePath);
3357
- const fetchSupport = takeEnum(config, ["fetchSupport"], ["native"], state, basePath);
3572
+ const fetchSupport = takeEnum(
3573
+ config,
3574
+ ["fetchSupport"],
3575
+ sdkTarget ? ["native", "node-fetch"] : ["native"],
3576
+ state,
3577
+ basePath
3578
+ );
3358
3579
  const namingObject = takeObject(config, ["naming"], state, basePath);
3359
3580
  const nestedNamespace = namingObject ? takeString(namingObject.value, ["namespace"], state, namingObject.path) : void 0;
3581
+ const sdkOnlyOptions = sdkTarget ? {
3582
+ formDataSupport: takeEnum(
3583
+ config,
3584
+ ["formDataSupport"],
3585
+ ["Node16", "Node18"],
3586
+ state,
3587
+ basePath
3588
+ ),
3589
+ allowExtraFields: takeBoolean(config, ["allowExtraFields"], state, basePath),
3590
+ generateSubpackageExports: takeBoolean(
3591
+ config,
3592
+ ["generateSubpackageExports"],
3593
+ state,
3594
+ basePath
3595
+ ),
3596
+ exportAllRequestsAtRoot: takeBoolean(config, ["exportAllRequestsAtRoot"], state, basePath),
3597
+ generateEndpointMetadata: takeBoolean(
3598
+ config,
3599
+ ["generateEndpointMetadata"],
3600
+ state,
3601
+ basePath
3602
+ ),
3603
+ outputEsm: takeBoolean(config, ["outputEsm"], state, basePath),
3604
+ neverThrowErrors: takeBoolean(config, ["neverThrowErrors"], state, basePath),
3605
+ includeOtherInUnionTypes: takeBoolean(
3606
+ config,
3607
+ ["includeOtherInUnionTypes"],
3608
+ state,
3609
+ basePath
3610
+ ),
3611
+ includeUtilsOnUnionMembers: takeBoolean(
3612
+ config,
3613
+ ["includeUtilsOnUnionMembers"],
3614
+ state,
3615
+ basePath
3616
+ ),
3617
+ omitFernHeaders: takeBoolean(config, ["omitFernHeaders"], state, basePath),
3618
+ linter: takeEnum(config, ["linter"], ["biome", "oxlint", "none"], state, basePath),
3619
+ formatter: takeEnum(
3620
+ config,
3621
+ ["formatter"],
3622
+ ["prettier", "biome", "oxfmt", "none"],
3623
+ state,
3624
+ basePath
3625
+ )
3626
+ } : {};
3360
3627
  return compact({
3628
+ ...sdkOnlyOptions,
3629
+ // Only the manifest writes these keys; nothing in sdkOnlyOptions or below shares a name.
3630
+ ...manifestOptions,
3361
3631
  typescriptVersion: takeString(config, ["typescriptVersion"], state, basePath),
3362
3632
  zodVersion: takeString(config, ["zodVersion"], state, basePath),
3363
3633
  packageManager: takeEnum(config, ["packageManager"], ["pnpm", "yarn"], state, basePath),
3364
3634
  packageManagerVersion,
3365
3635
  testFramework: takeEnum(config, ["testFramework"], ["jest", "vitest"], state, basePath),
3366
- httpClient: fetchSupport === "native" ? { name: "fetch" } : void 0,
3636
+ httpClient: fetchSupport === void 0 ? void 0 : { name: fetchSupport === "native" ? "fetch" : "node-fetch" },
3367
3637
  bundle: takeBoolean(config, ["bundle"], state, basePath),
3368
3638
  exportClassDefault: takeBoolean(config, ["exportClassDefault"], state, basePath),
3369
3639
  namespaceExportName: takeString(config, ["namespaceExportName", "namespaceExport"], state, basePath) ?? nestedNamespace,
@@ -3760,6 +4030,83 @@ function mapPackageManifest(config, aliases, state, packageConfig, basePath) {
3760
4030
  );
3761
4031
  return manifest;
3762
4032
  }
4033
+ function mapPackageManifestOptions(manifest, state, packageConfig) {
4034
+ const bugs = takeManifestBugs(manifest, state);
4035
+ if (bugs) packageConfig.bugs = bugs;
4036
+ const repository = takeManifestRepository(manifest, state);
4037
+ if (repository !== void 0 && packageConfig.repository === void 0) {
4038
+ packageConfig.repository = repository;
4039
+ }
4040
+ const exports = takeObject(manifest.value, ["exports"], state, manifest.path);
4041
+ if (exports) consume(state, exports.path);
4042
+ return compact({
4043
+ exports: exports?.value,
4044
+ scripts: takeManifestScripts(manifest, state),
4045
+ files: takeStringArray(manifest.value, ["files"], state, manifest.path),
4046
+ engines: takeManifestStringRecord(manifest, "engines", state),
4047
+ lintStaged: takeManifestLintStaged(manifest, state),
4048
+ browser: takeManifestBrowser(manifest, state)
4049
+ });
4050
+ }
4051
+ function takeManifestBugs(manifest, state) {
4052
+ const text = takeString(manifest.value, ["bugs"], state, manifest.path);
4053
+ if (text !== void 0) return { url: text };
4054
+ const located = takeObject(manifest.value, ["bugs"], state, manifest.path);
4055
+ if (!located) return void 0;
4056
+ const url = takeString(located.value, ["url"], state, located.path);
4057
+ const email = takeString(located.value, ["email"], state, located.path);
4058
+ return url === void 0 && email === void 0 ? void 0 : compact({ url, email });
4059
+ }
4060
+ function takeManifestRepository(manifest, state) {
4061
+ const text = takeString(manifest.value, ["repository"], state, manifest.path);
4062
+ if (text !== void 0) return text;
4063
+ const located = takeObject(manifest.value, ["repository"], state, manifest.path);
4064
+ if (!located) return void 0;
4065
+ const url = takeString(located.value, ["url"], state, located.path);
4066
+ if (url !== void 0) void takeEnum(located.value, ["type"], ["git"], state, located.path);
4067
+ return url;
4068
+ }
4069
+ function takeManifestScripts(manifest, state) {
4070
+ const located = takeObject(manifest.value, ["scripts"], state, manifest.path);
4071
+ if (!located) return void 0;
4072
+ const scripts = Object.entries(located.value).flatMap(([name, command]) => {
4073
+ if (typeof command !== "string" || !command) return [];
4074
+ consume(state, [...located.path, name]);
4075
+ return [{ name, command }];
4076
+ });
4077
+ return scripts.length ? scripts : void 0;
4078
+ }
4079
+ function takeManifestStringRecord(manifest, key, state) {
4080
+ const located = takeObject(manifest.value, [key], state, manifest.path);
4081
+ if (!located) return void 0;
4082
+ const entries = Object.entries(located.value).flatMap(([name, value]) => {
4083
+ if (typeof value !== "string" || !value) return [];
4084
+ consume(state, [...located.path, name]);
4085
+ return [[name, value]];
4086
+ });
4087
+ return entries.length ? Object.fromEntries(entries) : void 0;
4088
+ }
4089
+ function takeManifestLintStaged(manifest, state) {
4090
+ const located = takeObject(manifest.value, ["lint-staged"], state, manifest.path);
4091
+ if (!located) return void 0;
4092
+ const entries = Object.entries(located.value).flatMap(([glob, commands]) => {
4093
+ const list = typeof commands === "string" ? [commands] : stringArrayValue(commands);
4094
+ if (!list?.length || list.some((command) => !command)) return [];
4095
+ consume(state, [...located.path, glob]);
4096
+ return [[glob, list]];
4097
+ });
4098
+ return entries.length ? Object.fromEntries(entries) : void 0;
4099
+ }
4100
+ function takeManifestBrowser(manifest, state) {
4101
+ const located = takeObject(manifest.value, ["browser"], state, manifest.path);
4102
+ if (!located) return void 0;
4103
+ const entries = Object.entries(located.value).flatMap(([name, value]) => {
4104
+ if (typeof value !== "boolean" && (typeof value !== "string" || !value)) return [];
4105
+ consume(state, [...located.path, name]);
4106
+ return [[name, value]];
4107
+ });
4108
+ return entries.length ? Object.fromEntries(entries) : void 0;
4109
+ }
3763
4110
  function takeManifestAuthors(manifest, state) {
3764
4111
  const located = locate(manifest.value, ["authors"], manifest.path);
3765
4112
  if (!located || !Array.isArray(located.value)) return [];
@@ -4144,6 +4491,7 @@ var publishConfigSchema = z.discriminatedUnion("registry", [
4144
4491
  z.strictObject({
4145
4492
  registry: z.literal("rubygems").describe("Publishes the package to a RubyGems-compatible registry."),
4146
4493
  apiKeyEnvironmentVariable: nonEmptyStringSchema.optional().describe("The environment variable containing the RubyGems API key."),
4494
+ ...trustedPublishingShape,
4147
4495
  ...commonPublishShape2
4148
4496
  }),
4149
4497
  z.strictObject({
@@ -4214,7 +4562,7 @@ var githubOutputSchema2 = z.strictObject({
4214
4562
  var distributionShape = {
4215
4563
  /** Valid only for `cli` targets with GitHub delivery; secret names only, never values. */
4216
4564
  distribution: cliDistributionConfigSchema.optional().describe(
4217
- "CLI release channels published by the generated release workflow; credentials are GitHub Actions secret names."
4565
+ "CLI release channels and build attestation settings for the generated release workflow; credentials are GitHub Actions secret names."
4218
4566
  )
4219
4567
  };
4220
4568
  var credentialResolutionShape = {