@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
@@ -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)) {
@@ -2186,6 +2301,29 @@ var mavenSignatureSchema = z.strictObject({
2186
2301
  "The armored secret key, or an environment expression that resolves to it."
2187
2302
  )
2188
2303
  });
2304
+ function apiKeySecretSchema(registry, example) {
2305
+ return nonEmptyStringSchema.regex(
2306
+ /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/,
2307
+ `${registry} apiKey must be an environment expression naming a secret, such as ${example}`
2308
+ );
2309
+ }
2310
+ function apiKeySecretDescription(registry, example) {
2311
+ 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.`;
2312
+ }
2313
+ function trustedPublishingSchema(registry) {
2314
+ return z.boolean().optional().describe(
2315
+ `Whether ${registry} publication uses trusted publishing (OIDC) instead of an API key secret.`
2316
+ );
2317
+ }
2318
+ function refineApiKeyAgainstTrustedPublishing(publish, context) {
2319
+ if (publish.trustedPublishing === true && publish.apiKey !== void 0) {
2320
+ context.addIssue({
2321
+ code: "custom",
2322
+ message: "apiKey must be omitted when trustedPublishing is true",
2323
+ path: ["apiKey"]
2324
+ });
2325
+ }
2326
+ }
2189
2327
  var commonPublishShape = {
2190
2328
  url: nonEmptyStringSchema.optional().describe("A custom package registry URL."),
2191
2329
  releaseBranch: nonEmptyStringSchema.optional().describe("The branch used by generated package release workflows."),
@@ -2221,12 +2359,16 @@ var sdkConfigV1PublishConfigSchema = z.discriminatedUnion("registry", [
2221
2359
  }),
2222
2360
  z.strictObject({
2223
2361
  registry: z.literal("nuget").describe("Publishes the package to a NuGet-compatible registry."),
2362
+ apiKey: apiKeySecretSchema("NuGet", "${NUGET_API_KEY}").optional().describe(apiKeySecretDescription("NuGet", "${NUGET_API_KEY}")),
2363
+ trustedPublishing: trustedPublishingSchema("NuGet"),
2224
2364
  ...commonPublishShape
2225
- }),
2365
+ }).superRefine(refineApiKeyAgainstTrustedPublishing),
2226
2366
  z.strictObject({
2227
2367
  registry: z.literal("rubygems").describe("Publishes the package to a RubyGems-compatible registry."),
2368
+ apiKey: apiKeySecretSchema("RubyGems", "${RUBYGEMS_API_KEY}").optional().describe(apiKeySecretDescription("RubyGems", "${RUBYGEMS_API_KEY}")),
2369
+ trustedPublishing: trustedPublishingSchema("RubyGems"),
2228
2370
  ...commonPublishShape
2229
- }),
2371
+ }).superRefine(refineApiKeyAgainstTrustedPublishing),
2230
2372
  z.strictObject({
2231
2373
  registry: z.literal("crates").describe("Publishes the crate to a Cargo-compatible registry."),
2232
2374
  token: nonEmptyStringSchema.optional().describe("The Cargo registry token, or an environment expression that resolves to it."),
@@ -2284,7 +2426,7 @@ var sdkConfigV1OutputConfigSchema = z.discriminatedUnion(
2284
2426
  );
2285
2427
  var cliDistributionShape = {
2286
2428
  distribution: cliDistributionConfigSchema.optional().describe(
2287
- "CLI release channels published by the generated release workflow; credentials are GitHub Actions secret names."
2429
+ "CLI release channels and build attestation settings for the generated release workflow; credentials are GitHub Actions secret names."
2288
2430
  )
2289
2431
  };
2290
2432
  function rejectDistributionWithoutGithub(output, context) {
@@ -2330,6 +2472,9 @@ var apiImportSettingsSchema = z.strictObject({
2330
2472
  discriminatedUnionV2: z.boolean().optional().describe("Whether oneOf and anyOf schemas use the newer discriminator inference rules."),
2331
2473
  undiscriminatedUnionsWithLiterals: z.boolean().optional().describe("Whether literal-bearing unions remain undiscriminated during import."),
2332
2474
  inlineAllOfSchemas: z.boolean().optional().describe("Whether schemas composed with allOf are inlined when possible."),
2475
+ inferDiscriminatedUnionBaseProperties: z.boolean().optional().describe(
2476
+ "Whether properties every discriminated-union variant shares are lifted onto the union."
2477
+ ),
2333
2478
  resolveSchemaCollisions: z.boolean().optional().describe("Whether colliding imported schema names are resolved automatically."),
2334
2479
  asyncApiMessageNaming: z.enum(["v1", "v2"]).optional().describe("The naming strategy used for messages imported from AsyncAPI."),
2335
2480
  defaultIntegerFormat: z.enum(["int32", "int64", "uint32", "uint64"]).optional().describe("The integer format used when a schema does not declare one.")
@@ -2428,7 +2573,7 @@ var packageScriptSchema = z.strictObject({
2428
2573
  command: nonEmptyStringSchema.describe("The command executed by the package script.")
2429
2574
  });
2430
2575
  var httpClientSchema = z.strictObject({
2431
- name: z.enum(["axios", "fetch"]).describe("The HTTP client used by generated TypeScript code.")
2576
+ name: z.enum(["axios", "fetch", "node-fetch"]).describe("The HTTP client used by generated TypeScript code.")
2432
2577
  });
2433
2578
  var typescriptGenerationConfigSchema = z.strictObject({
2434
2579
  typescriptVersion: nonEmptyStringSchema.optional().describe("The TypeScript version used by the generated project."),
@@ -2444,6 +2589,37 @@ var typescriptGenerationConfigSchema = z.strictObject({
2444
2589
  /** Name of the top-level namespace export emitted by Fern-compatible TypeScript SDKs. */
2445
2590
  namespaceExportName: nonEmptyStringSchema.optional().describe("The top-level namespace export name."),
2446
2591
  consolidateTypeFiles: z.boolean().optional().describe("Whether generated types are consolidated into fewer source files."),
2592
+ formDataSupport: z.enum(["Node16", "Node18"]).optional().describe(
2593
+ "The multipart form-data implementation: Node16 uses the form-data packages, Node18 uses the platform FormData."
2594
+ ),
2595
+ allowExtraFields: z.boolean().optional().describe(
2596
+ "Whether response parsing keeps unrecognized object keys and accepts unrecognized union members and enum values."
2597
+ ),
2598
+ generateSubpackageExports: z.boolean().optional().describe(
2599
+ "Whether the package manifest exports each subpackage client as its own entry point."
2600
+ ),
2601
+ exportAllRequestsAtRoot: z.boolean().optional().describe("Whether every request type is also exported from the package root."),
2602
+ generateEndpointMetadata: z.boolean().optional().describe("Whether generated clients expose metadata for each endpoint."),
2603
+ outputEsm: z.boolean().optional().describe(
2604
+ "Whether the package is marked as an ES module while still shipping its CommonJS build."
2605
+ ),
2606
+ neverThrowErrors: z.boolean().optional().describe(
2607
+ "Whether client methods return a wrapped response instead of throwing on a non-2xx status."
2608
+ ),
2609
+ includeOtherInUnionTypes: z.boolean().optional().describe("Whether union types gain a member for variants the SDK does not recognize."),
2610
+ includeUtilsOnUnionMembers: z.boolean().optional().describe("Whether helper utilities are generated on each member of a union type."),
2611
+ omitFernHeaders: z.boolean().optional().describe("Whether requests omit the SDK identification headers and the User-Agent header."),
2612
+ linter: z.enum(["biome", "oxlint", "none"]).optional().describe("The linter configured in the generated project."),
2613
+ formatter: z.enum(["prettier", "biome", "oxfmt", "none"]).optional().describe("The formatter configured in the generated project."),
2614
+ exports: jsonObjectSchema.optional().describe(
2615
+ "package.json exports entries, keyed by subpath, applied over the generated exports. Each subpath replaces the generated one."
2616
+ ),
2617
+ files: z.array(nonEmptyStringSchema).optional().describe("package.json files entries, listed ahead of the generated ones."),
2618
+ engines: z.record(nonEmptyStringSchema, nonEmptyStringSchema).optional().describe("package.json engines version ranges, keyed by engine name such as node."),
2619
+ lintStaged: z.record(nonEmptyStringSchema, z.array(nonEmptyStringSchema).min(1)).optional().describe("lint-staged commands to run, keyed by file glob."),
2620
+ browser: z.record(nonEmptyStringSchema, z.union([z.boolean(), nonEmptyStringSchema])).optional().describe(
2621
+ "package.json browser field: a module or file is excluded (false) or replaced by another path."
2622
+ ),
2447
2623
  allowCustomFetcher: z.boolean().optional().describe("Whether callers can supply a custom fetch implementation."),
2448
2624
  useBrandedStringAliases: z.boolean().optional().describe("Whether string aliases are emitted as distinct branded types."),
2449
2625
  useLegacyExports: z.boolean().optional().describe("Whether the package emits the legacy export layout."),
@@ -2517,7 +2693,7 @@ var pythonGenerationConfigSchema = z.strictObject({
2517
2693
  additionalInitExports: z.array(additionalInitExportSchema).optional().describe("Additional names re-exported from the package initializer.")
2518
2694
  });
2519
2695
  var rubyGenerationConfigSchema = z.strictObject({
2520
- requirePaths: z.array(nonEmptyStringSchema).optional().describe("Ruby load paths included in the generated gem specification.")
2696
+ requirePaths: z.array(nonEmptyStringSchema).optional().describe("Files the generated entry file loads with `require`.")
2521
2697
  });
2522
2698
  var rustGenerationConfigSchema = z.strictObject({
2523
2699
  /**
@@ -2859,16 +3035,21 @@ function mapFernConfigToSdkConfigV1(input) {
2859
3035
  ...optional("client", clients.targetOverrides[index]),
2860
3036
  ...optional("docs", docs.targetOverrides[index])
2861
3037
  };
2862
- const targetPackage = { ...invocation.package, ...outputPackage, ...generator.package };
3038
+ const mappedPackage = { ...invocation.package, ...outputPackage, ...generator.package };
2863
3039
  invocation.unsupportedFields.push(
2864
3040
  ...resolveTargetPackage(
2865
- targetPackage,
3041
+ mappedPackage,
2866
3042
  invocation.packageFallbacks,
2867
3043
  generator,
2868
3044
  output.delivery,
2869
3045
  index
2870
3046
  )
2871
3047
  );
3048
+ const targetPackage = withRustCratesPublishDefaults(mappedPackage, {
3049
+ language,
3050
+ output,
3051
+ generator
3052
+ });
2872
3053
  if (Object.keys(targetPackage).length > 0 || outputPackage || generator.package) {
2873
3054
  target.package = targetPackage;
2874
3055
  }
@@ -2937,6 +3118,21 @@ function mapFernConfigToSdkConfigV1(input) {
2937
3118
  ])
2938
3119
  };
2939
3120
  }
3121
+ function withRustCratesPublishDefaults(packageConfig, { language, output, generator }) {
3122
+ if (language !== "rust" || !("publish" in output) || output.publish?.registry !== "crates") {
3123
+ return packageConfig;
3124
+ }
3125
+ const { packageName } = packageConfig;
3126
+ return {
3127
+ ...packageName ? { description: `Rust SDK for ${packageName} generated by Fern` } : {},
3128
+ ...usesCustomLicenseFile(generator) ? {} : { license: { type: "MIT" } },
3129
+ ...packageConfig
3130
+ };
3131
+ }
3132
+ function usesCustomLicenseFile({ config, raw }) {
3133
+ const metadata = isObject(raw) && isObject(raw.metadata) ? raw.metadata : void 0;
3134
+ return isObject(config) && typeof config.packageLicenseFile === "string" || isObject(metadata?.license);
3135
+ }
2940
3136
  function resolveLanguage(generator, index) {
2941
3137
  const language = generator.sdkLanguage ?? generatorLanguages[generator.name];
2942
3138
  if (!language || !isFernLanguage(language)) {
@@ -3075,6 +3271,13 @@ function mapInvocation(generator, language, activeLanguages, index) {
3075
3271
  }
3076
3272
  }
3077
3273
  const docs = mapReadme(generator.readme, language, activeLanguages, state, [...prefix, "readme"]);
3274
+ if (language === "typescript") {
3275
+ const includeApiReference = takeBoolean(config, ["includeApiReference"], state, [
3276
+ ...prefix,
3277
+ "config"
3278
+ ]);
3279
+ if (includeApiReference !== void 0) docs.includeApiReference = includeApiReference;
3280
+ }
3078
3281
  const unsupportedFields = [
3079
3282
  ...state.valueDiagnostics ?? [],
3080
3283
  ...collectUnsupported(config, [...prefix, "config"], state),
@@ -3221,7 +3424,12 @@ function mapCommonConfig(language, config, state, client, generation, packageCon
3221
3424
  }
3222
3425
  const pythonClient = language === "python" ? takeObject(config, ["client"], state, basePath) : void 0;
3223
3426
  const typescriptNaming = language === "typescript" || language === "mcp" ? takeObject(config, ["naming"], state, basePath) : void 0;
3224
- const directClientName = takeString(config, ["clientName", "clientClassName"], state, basePath);
3427
+ const directClientName = takeString(
3428
+ config,
3429
+ language === "ruby" ? ["clientName"] : ["clientName", "clientClassName"],
3430
+ state,
3431
+ basePath
3432
+ );
3225
3433
  const nestedClientName = pythonClient ? takeString(pythonClient.value, ["className"], state, pythonClient.path) : typescriptNaming ? takeString(typescriptNaming.value, ["client"], state, typescriptNaming.path) : language === "ruby" ? (
3226
3434
  // Fern's Ruby generator renames the root client class (`Auth0::Management`) with it.
3227
3435
  takeString(config, ["clientModuleName"], state, basePath)
@@ -3255,7 +3463,13 @@ function mapCommonConfig(language, config, state, client, generation, packageCon
3255
3463
  });
3256
3464
  if (Object.keys(naming).length) generation.naming = naming;
3257
3465
  const wireTests = takeBoolean(config, ["enableWireTests", "generateWireTests"], state, basePath);
3258
- if (wireTests !== void 0) generation.wireTests = { enabled: wireTests };
3466
+ const wireTestsFallback = language === "typescript" ? takeBoolean(config, ["wireTestsFallbackToAutoGeneratedErrorExamples"], state, basePath) : void 0;
3467
+ if (wireTests !== void 0 || wireTestsFallback !== void 0) {
3468
+ generation.wireTests = { enabled: wireTests ?? true };
3469
+ if (wireTestsFallback !== void 0) {
3470
+ generation.wireTests.fallbackToGeneratedErrorExamples = wireTestsFallback;
3471
+ }
3472
+ }
3259
3473
  const webSockets = takeBoolean(
3260
3474
  config,
3261
3475
  ["generateWebSocketClients", "shouldGenerateWebsocketClients", "enableWebsockets"],
@@ -3298,10 +3512,10 @@ function mapTypescriptStreams(config, state, basePath) {
3298
3512
  function mapLanguageConfig(language, config, state, packageConfig, basePath, targetIndex) {
3299
3513
  switch (language) {
3300
3514
  case "typescript":
3301
- return mapTypescript(config, state, packageConfig, basePath);
3515
+ return mapTypescript(config, state, packageConfig, basePath, true);
3302
3516
  case "mcp":
3303
3517
  return {
3304
- ...mapTypescript(config, state, packageConfig, basePath),
3518
+ ...mapTypescript(config, state, packageConfig, basePath, false),
3305
3519
  ...mapMcp(config, state, basePath)
3306
3520
  };
3307
3521
  case "python":
@@ -3327,7 +3541,7 @@ function mapLanguageConfig(language, config, state, packageConfig, basePath, tar
3327
3541
  return mapTerraform(config, state, packageConfig, basePath);
3328
3542
  }
3329
3543
  }
3330
- function mapTypescript(config, state, packageConfig, basePath) {
3544
+ function mapTypescript(config, state, packageConfig, basePath, sdkTarget) {
3331
3545
  const packageJsonManifest = mapPackageManifest(
3332
3546
  config,
3333
3547
  ["packageJson"],
@@ -3340,18 +3554,74 @@ function mapTypescript(config, state, packageConfig, basePath) {
3340
3554
  const packageManagerVersionPart = packageManagerAtIndex >= 0 ? packageJsonPackageManager?.slice(packageManagerAtIndex + 1).split("+")[0] : void 0;
3341
3555
  const packageManagerVersion = packageManagerVersionPart || void 0;
3342
3556
  if (packageJsonManifest) liftPackageJsonDependencies(packageJsonManifest, state, packageConfig);
3557
+ const manifestOptions = sdkTarget && packageJsonManifest ? mapPackageManifestOptions(packageJsonManifest, state, packageConfig) : {};
3343
3558
  const serdeLayer = takeBoolean(config, ["serdeLayer"], state, basePath);
3344
3559
  const noSerdeLayer = takeBoolean(config, ["noSerdeLayer"], state, basePath);
3345
- const fetchSupport = takeEnum(config, ["fetchSupport"], ["native"], state, basePath);
3560
+ const fetchSupport = takeEnum(
3561
+ config,
3562
+ ["fetchSupport"],
3563
+ sdkTarget ? ["native", "node-fetch"] : ["native"],
3564
+ state,
3565
+ basePath
3566
+ );
3346
3567
  const namingObject = takeObject(config, ["naming"], state, basePath);
3347
3568
  const nestedNamespace = namingObject ? takeString(namingObject.value, ["namespace"], state, namingObject.path) : void 0;
3569
+ const sdkOnlyOptions = sdkTarget ? {
3570
+ formDataSupport: takeEnum(
3571
+ config,
3572
+ ["formDataSupport"],
3573
+ ["Node16", "Node18"],
3574
+ state,
3575
+ basePath
3576
+ ),
3577
+ allowExtraFields: takeBoolean(config, ["allowExtraFields"], state, basePath),
3578
+ generateSubpackageExports: takeBoolean(
3579
+ config,
3580
+ ["generateSubpackageExports"],
3581
+ state,
3582
+ basePath
3583
+ ),
3584
+ exportAllRequestsAtRoot: takeBoolean(config, ["exportAllRequestsAtRoot"], state, basePath),
3585
+ generateEndpointMetadata: takeBoolean(
3586
+ config,
3587
+ ["generateEndpointMetadata"],
3588
+ state,
3589
+ basePath
3590
+ ),
3591
+ outputEsm: takeBoolean(config, ["outputEsm"], state, basePath),
3592
+ neverThrowErrors: takeBoolean(config, ["neverThrowErrors"], state, basePath),
3593
+ includeOtherInUnionTypes: takeBoolean(
3594
+ config,
3595
+ ["includeOtherInUnionTypes"],
3596
+ state,
3597
+ basePath
3598
+ ),
3599
+ includeUtilsOnUnionMembers: takeBoolean(
3600
+ config,
3601
+ ["includeUtilsOnUnionMembers"],
3602
+ state,
3603
+ basePath
3604
+ ),
3605
+ omitFernHeaders: takeBoolean(config, ["omitFernHeaders"], state, basePath),
3606
+ linter: takeEnum(config, ["linter"], ["biome", "oxlint", "none"], state, basePath),
3607
+ formatter: takeEnum(
3608
+ config,
3609
+ ["formatter"],
3610
+ ["prettier", "biome", "oxfmt", "none"],
3611
+ state,
3612
+ basePath
3613
+ )
3614
+ } : {};
3348
3615
  return compact({
3616
+ ...sdkOnlyOptions,
3617
+ // Only the manifest writes these keys; nothing in sdkOnlyOptions or below shares a name.
3618
+ ...manifestOptions,
3349
3619
  typescriptVersion: takeString(config, ["typescriptVersion"], state, basePath),
3350
3620
  zodVersion: takeString(config, ["zodVersion"], state, basePath),
3351
3621
  packageManager: takeEnum(config, ["packageManager"], ["pnpm", "yarn"], state, basePath),
3352
3622
  packageManagerVersion,
3353
3623
  testFramework: takeEnum(config, ["testFramework"], ["jest", "vitest"], state, basePath),
3354
- httpClient: fetchSupport === "native" ? { name: "fetch" } : void 0,
3624
+ httpClient: fetchSupport === void 0 ? void 0 : { name: fetchSupport === "native" ? "fetch" : "node-fetch" },
3355
3625
  bundle: takeBoolean(config, ["bundle"], state, basePath),
3356
3626
  exportClassDefault: takeBoolean(config, ["exportClassDefault"], state, basePath),
3357
3627
  namespaceExportName: takeString(config, ["namespaceExportName", "namespaceExport"], state, basePath) ?? nestedNamespace,
@@ -3748,6 +4018,83 @@ function mapPackageManifest(config, aliases, state, packageConfig, basePath) {
3748
4018
  );
3749
4019
  return manifest;
3750
4020
  }
4021
+ function mapPackageManifestOptions(manifest, state, packageConfig) {
4022
+ const bugs = takeManifestBugs(manifest, state);
4023
+ if (bugs) packageConfig.bugs = bugs;
4024
+ const repository = takeManifestRepository(manifest, state);
4025
+ if (repository !== void 0 && packageConfig.repository === void 0) {
4026
+ packageConfig.repository = repository;
4027
+ }
4028
+ const exports = takeObject(manifest.value, ["exports"], state, manifest.path);
4029
+ if (exports) consume(state, exports.path);
4030
+ return compact({
4031
+ exports: exports?.value,
4032
+ scripts: takeManifestScripts(manifest, state),
4033
+ files: takeStringArray(manifest.value, ["files"], state, manifest.path),
4034
+ engines: takeManifestStringRecord(manifest, "engines", state),
4035
+ lintStaged: takeManifestLintStaged(manifest, state),
4036
+ browser: takeManifestBrowser(manifest, state)
4037
+ });
4038
+ }
4039
+ function takeManifestBugs(manifest, state) {
4040
+ const text = takeString(manifest.value, ["bugs"], state, manifest.path);
4041
+ if (text !== void 0) return { url: text };
4042
+ const located = takeObject(manifest.value, ["bugs"], state, manifest.path);
4043
+ if (!located) return void 0;
4044
+ const url = takeString(located.value, ["url"], state, located.path);
4045
+ const email = takeString(located.value, ["email"], state, located.path);
4046
+ return url === void 0 && email === void 0 ? void 0 : compact({ url, email });
4047
+ }
4048
+ function takeManifestRepository(manifest, state) {
4049
+ const text = takeString(manifest.value, ["repository"], state, manifest.path);
4050
+ if (text !== void 0) return text;
4051
+ const located = takeObject(manifest.value, ["repository"], state, manifest.path);
4052
+ if (!located) return void 0;
4053
+ const url = takeString(located.value, ["url"], state, located.path);
4054
+ if (url !== void 0) void takeEnum(located.value, ["type"], ["git"], state, located.path);
4055
+ return url;
4056
+ }
4057
+ function takeManifestScripts(manifest, state) {
4058
+ const located = takeObject(manifest.value, ["scripts"], state, manifest.path);
4059
+ if (!located) return void 0;
4060
+ const scripts = Object.entries(located.value).flatMap(([name, command]) => {
4061
+ if (typeof command !== "string" || !command) return [];
4062
+ consume(state, [...located.path, name]);
4063
+ return [{ name, command }];
4064
+ });
4065
+ return scripts.length ? scripts : void 0;
4066
+ }
4067
+ function takeManifestStringRecord(manifest, key, state) {
4068
+ const located = takeObject(manifest.value, [key], state, manifest.path);
4069
+ if (!located) return void 0;
4070
+ const entries = Object.entries(located.value).flatMap(([name, value]) => {
4071
+ if (typeof value !== "string" || !value) return [];
4072
+ consume(state, [...located.path, name]);
4073
+ return [[name, value]];
4074
+ });
4075
+ return entries.length ? Object.fromEntries(entries) : void 0;
4076
+ }
4077
+ function takeManifestLintStaged(manifest, state) {
4078
+ const located = takeObject(manifest.value, ["lint-staged"], state, manifest.path);
4079
+ if (!located) return void 0;
4080
+ const entries = Object.entries(located.value).flatMap(([glob, commands]) => {
4081
+ const list = typeof commands === "string" ? [commands] : stringArrayValue(commands);
4082
+ if (!list?.length || list.some((command) => !command)) return [];
4083
+ consume(state, [...located.path, glob]);
4084
+ return [[glob, list]];
4085
+ });
4086
+ return entries.length ? Object.fromEntries(entries) : void 0;
4087
+ }
4088
+ function takeManifestBrowser(manifest, state) {
4089
+ const located = takeObject(manifest.value, ["browser"], state, manifest.path);
4090
+ if (!located) return void 0;
4091
+ const entries = Object.entries(located.value).flatMap(([name, value]) => {
4092
+ if (typeof value !== "boolean" && (typeof value !== "string" || !value)) return [];
4093
+ consume(state, [...located.path, name]);
4094
+ return [[name, value]];
4095
+ });
4096
+ return entries.length ? Object.fromEntries(entries) : void 0;
4097
+ }
3751
4098
  function takeManifestAuthors(manifest, state) {
3752
4099
  const located = locate(manifest.value, ["authors"], manifest.path);
3753
4100
  if (!located || !Array.isArray(located.value)) return [];