@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
@@ -438,6 +438,12 @@ var dependencySchema = zod.z.strictObject({
438
438
  }).refine(({ source, version }) => source !== void 0 || version !== void 0, {
439
439
  message: "dependency must define a version or a source"
440
440
  });
441
+ var bugsSchema = zod.z.strictObject({
442
+ url: nonEmptyStringSchema.optional().describe("The issue tracker URL."),
443
+ email: nonEmptyStringSchema.optional().describe("The address for reporting issues.")
444
+ }).refine(({ url, email }) => url !== void 0 || email !== void 0, {
445
+ message: "bugs must define a url or an email"
446
+ });
441
447
  var projectUrlSchema = zod.z.strictObject({
442
448
  label: nonEmptyStringSchema.describe("The label displayed for the project link."),
443
449
  url: nonEmptyStringSchema.describe("The project link URL.")
@@ -452,6 +458,7 @@ var packageConfigSchema = zod.z.strictObject({
452
458
  description: zod.z.string().optional().describe("The package description used in generated metadata."),
453
459
  repository: nonEmptyStringSchema.optional().describe("The source repository for the generated SDK."),
454
460
  homepage: nonEmptyStringSchema.optional().describe("The package homepage URL."),
461
+ bugs: bugsSchema.optional().describe("Where issues with the package are reported."),
455
462
  documentationUrl: nonEmptyStringSchema.optional().describe("The package documentation URL."),
456
463
  authors: zod.z.array(authorSchema).optional().describe("Authors credited in package metadata."),
457
464
  developers: zod.z.array(developerSchema).optional().describe("Developers credited in package metadata."),
@@ -507,7 +514,10 @@ var cliDistributionConfigSchema = zod.z.strictObject({
507
514
  "A GitHub App used to mint short-lived tokens for every channel without its own tokenSecretName."
508
515
  ),
509
516
  homebrew: cliDistributionHomebrewSchema.optional().describe("Publishes a Homebrew formula to a tap repository on each tagged release."),
510
- scoop: cliDistributionScoopSchema.optional().describe("Publishes a Scoop manifest to a bucket repository on each tagged release.")
517
+ scoop: cliDistributionScoopSchema.optional().describe("Publishes a Scoop manifest to a bucket repository on each tagged release."),
518
+ githubAttestations: zod.z.union([zod.z.literal("auto"), zod.z.boolean()], { error: 'must be "auto", true, or false' }).optional().describe(
519
+ '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.'
520
+ )
511
521
  }).superRefine((distribution, context) => {
512
522
  if (distribution.homebrew !== void 0 || distribution.scoop !== void 0) {
513
523
  return;
@@ -520,6 +530,9 @@ var cliDistributionConfigSchema = zod.z.strictObject({
520
530
  });
521
531
  return;
522
532
  }
533
+ if (distribution.githubAttestations !== void 0) {
534
+ return;
535
+ }
523
536
  context.addIssue({
524
537
  code: "custom",
525
538
  message: "distribution must configure at least one channel: homebrew or scoop"
@@ -531,14 +544,17 @@ var CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE = "CLI_DISTRIBUTION_NON_RELEASE_MODE"
531
544
  var nonReleaseGithubModes = /* @__PURE__ */ new Set(["pull-request", "push", "commit"]);
532
545
  function cliDistributionModeDiagnostic(output, outputPath) {
533
546
  const mode = output?.github?.mode;
534
- if (output?.distribution === void 0 || mode === void 0 || !nonReleaseGithubModes.has(mode)) {
547
+ const distribution = output?.distribution;
548
+ if (distribution === void 0 || mode === void 0 || !nonReleaseGithubModes.has(mode)) {
535
549
  return void 0;
536
550
  }
551
+ const hasChannel = distribution.homebrew !== void 0 || distribution.scoop !== void 0;
552
+ 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.";
537
553
  return {
538
554
  code: CLI_DISTRIBUTION_NON_RELEASE_MODE_CODE,
539
555
  severity: "warning",
540
556
  path: [...outputPath, "github", "mode"],
541
- 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.`,
557
+ reason: `GitHub mode "${mode}" does not create a release tag, so ${consequence}`,
542
558
  suggestedAction: 'Use mode "release" or "commit-and-release", or push a version tag after merging generated changes.'
543
559
  };
544
560
  }
@@ -973,6 +989,7 @@ var DISTRIBUTION_KEYS = {
973
989
  homebrew: { tap: "tap", formula: "formula", tokenEnvironmentVariable: "tokenSecretName" },
974
990
  scoop: { bucket: "bucket", tokenEnvironmentVariable: "tokenSecretName" }
975
991
  };
992
+ var DISTRIBUTION_SCALAR_KEYS = ["githubAttestations"];
976
993
  function mapCliDistribution(config, state, basePath, targetIndex) {
977
994
  if (!Object.hasOwn(config, "distribution")) return;
978
995
  const raw = config.distribution;
@@ -1019,6 +1036,11 @@ function translateDistribution(distribution, path) {
1019
1036
  }
1020
1037
  value[block] = translatedBlock;
1021
1038
  }
1039
+ for (const key of DISTRIBUTION_SCALAR_KEYS) {
1040
+ if (!Object.hasOwn(distribution, key)) continue;
1041
+ value[key] = distribution[key];
1042
+ consumedPaths.push([...path, key]);
1043
+ }
1022
1044
  return { value, consumedPaths, legacyKeys };
1023
1045
  }
1024
1046
  function invalidDistributionDiagnostic(issue, path, legacyKeys, targetIndex) {
@@ -1158,6 +1180,7 @@ function mapOutput(generator, language, index) {
1158
1180
  output.publishV2,
1159
1181
  rawOutput,
1160
1182
  language,
1183
+ "zip",
1161
1184
  [...outputPath, "publishV2"],
1162
1185
  state,
1163
1186
  index
@@ -1179,6 +1202,7 @@ function mapOutput(generator, language, index) {
1179
1202
  output.registryOverrides,
1180
1203
  rawOutput,
1181
1204
  language,
1205
+ "zip",
1182
1206
  [...outputPath, "registryOverrides"],
1183
1207
  state,
1184
1208
  index
@@ -1206,7 +1230,14 @@ function mapOutput(generator, language, index) {
1206
1230
  }
1207
1231
  }
1208
1232
  function sanitizeNormalizedOutput(output, rawOutput, state, index) {
1209
- const publish = output.publish ? sanitizePublishConfig(output.publish, output.publish, rawOutput, state, index) : void 0;
1233
+ const publish = output.publish ? sanitizePublishConfig(
1234
+ output.publish,
1235
+ output.publish,
1236
+ rawOutput,
1237
+ output.delivery,
1238
+ state,
1239
+ index
1240
+ ) : void 0;
1210
1241
  const publishShape = publish ? { publish } : {};
1211
1242
  if (output.delivery !== "github") return { ...output, ...publishShape };
1212
1243
  const { credentials, ...github } = output.github;
@@ -1233,12 +1264,21 @@ function githubOutput(value, publishInfo, rawOutput, language, sourcePath, state
1233
1264
  );
1234
1265
  consume(state, [...sourcePath, "owner"]);
1235
1266
  consume(state, [...sourcePath, "repo"]);
1236
- const publication = publishInfo ? mapPublication(publishInfo, rawOutput, language, [...sourcePath, "publishInfo"], state, index) : void 0;
1267
+ const publication = publishInfo ? mapPublication(
1268
+ publishInfo,
1269
+ rawOutput,
1270
+ language,
1271
+ "github",
1272
+ [...sourcePath, "publishInfo"],
1273
+ state,
1274
+ index
1275
+ ) : void 0;
1237
1276
  const type = stringValue(value.type);
1238
1277
  const licensePackage = mapGithubLicense(value.license, [...sourcePath, "license"], state);
1239
1278
  const reviewers = mapReviewers(value.reviewers, [...sourcePath, "reviewers"], state);
1240
1279
  if (type) consume(state, [...sourcePath, "type"]);
1241
1280
  if (typeof value.makePr === "boolean") consume(state, [...sourcePath, "makePr"]);
1281
+ if (value.workflows === true) consume(state, [...sourcePath, "workflows"]);
1242
1282
  if (stringValue(value.host)) consume(state, [...sourcePath, "host"]);
1243
1283
  if (stringValue(value.branch)) consume(state, [...sourcePath, "branch"]);
1244
1284
  consumeCredentialObject(value.credentials, [...sourcePath, "credentials"], state);
@@ -1325,7 +1365,7 @@ function reviewerNames(value, expectedType, sourcePath, state) {
1325
1365
  });
1326
1366
  return names.length ? names : void 0;
1327
1367
  }
1328
- function mapPublication(value, rawOutput, language, sourcePath, state, index) {
1368
+ function mapPublication(value, rawOutput, language, delivery, sourcePath, state, index) {
1329
1369
  if (!isObject(value))
1330
1370
  fail(
1331
1371
  "FERN_PUBLICATION_INVALID",
@@ -1346,6 +1386,7 @@ function mapPublication(value, rawOutput, language, sourcePath, state, index) {
1346
1386
  compact({ registry, url: registryUrl }),
1347
1387
  nested,
1348
1388
  rawOutput,
1389
+ delivery,
1349
1390
  state,
1350
1391
  index
1351
1392
  );
@@ -1366,7 +1407,7 @@ function mapPublication(value, rawOutput, language, sourcePath, state, index) {
1366
1407
  ...packageName || Object.keys(metadata).length ? { package: { ...optional("packageName", packageName), ...metadata } } : {}
1367
1408
  };
1368
1409
  }
1369
- function sanitizePublishConfig(publish, resolvedPublication, rawOutput, state, index) {
1410
+ function sanitizePublishConfig(publish, resolvedPublication, rawOutput, delivery, state, index) {
1370
1411
  const rawPublication = rawOutput?.location === publish.registry ? rawOutput : void 0;
1371
1412
  const rawPath = ["group", "generators", index, "output"];
1372
1413
  if (publish.registry === "npm" || publish.registry === "crates") {
@@ -1462,6 +1503,33 @@ function sanitizePublishConfig(publish, resolvedPublication, rawOutput, state, i
1462
1503
  signature
1463
1504
  });
1464
1505
  }
1506
+ if (publish.registry === "nuget" || publish.registry === "rubygems") {
1507
+ const apiKey = mapApiKeyExpression(
1508
+ publish.registry,
1509
+ rawPublication,
1510
+ resolvedPublication,
1511
+ rawPath,
1512
+ state,
1513
+ index,
1514
+ fernDefaultApiKey(publish.registry, rawOutput, delivery)
1515
+ );
1516
+ warnForUnexpectedCredentialFields(
1517
+ rawPublication,
1518
+ resolvedPublication,
1519
+ ["apiKey"],
1520
+ rawPath,
1521
+ state,
1522
+ index
1523
+ );
1524
+ return compact({
1525
+ registry: publish.registry,
1526
+ url: publish.url,
1527
+ releaseBranch: publish.releaseBranch,
1528
+ tolerateRepublish: publish.tolerateRepublish,
1529
+ apiKey,
1530
+ trustedPublishing: usesTrustedPublishing(rawPublication) || void 0
1531
+ });
1532
+ }
1465
1533
  warnForUnrepresentableCredentials(rawPublication, resolvedPublication, rawPath, state, index);
1466
1534
  return compact({
1467
1535
  registry: publish.registry,
@@ -1470,11 +1538,19 @@ function sanitizePublishConfig(publish, resolvedPublication, rawOutput, state, i
1470
1538
  tolerateRepublish: publish.tolerateRepublish
1471
1539
  });
1472
1540
  }
1473
- function mapLegacyPublication(value, rawOutput, language, sourcePath, state, index) {
1541
+ function mapLegacyPublication(value, rawOutput, language, delivery, sourcePath, state, index) {
1474
1542
  const registry = defaultRegistry(language, sourcePath, index);
1475
1543
  const overrides = isObject(value) ? value : {};
1476
1544
  const override = isObject(overrides[registry]) ? overrides[registry] : {};
1477
- return mapPublication(override, rawOutput, language, [...sourcePath, registry], state, index);
1545
+ return mapPublication(
1546
+ override,
1547
+ rawOutput,
1548
+ language,
1549
+ delivery,
1550
+ [...sourcePath, registry],
1551
+ state,
1552
+ index
1553
+ );
1478
1554
  }
1479
1555
  function mapCredentialExpression(rawPublication, resolvedPublication, field, rawPath, state, index) {
1480
1556
  const rawValue = rawPublication?.[field];
@@ -1489,6 +1565,45 @@ function mapCredentialExpression(rawPublication, resolvedPublication, field, raw
1489
1565
  }
1490
1566
  return void 0;
1491
1567
  }
1568
+ var TRUSTED_PUBLISHING_VALUES = /* @__PURE__ */ new Set(["OIDC", "<USE_OIDC>"]);
1569
+ function rawApiKeyValue(raw) {
1570
+ return raw?.apiKey ?? raw?.["api-key"];
1571
+ }
1572
+ function usesTrustedPublishing(rawPublication) {
1573
+ const rawValue = rawApiKeyValue(rawPublication);
1574
+ return typeof rawValue === "string" && TRUSTED_PUBLISHING_VALUES.has(rawValue);
1575
+ }
1576
+ function mapApiKeyExpression(registry, rawPublication, resolvedPublication, rawPath, state, index, defaultSecretName) {
1577
+ const rawValue = rawApiKeyValue(rawPublication);
1578
+ if (isEnvironmentExpression(rawValue)) return rawValue;
1579
+ if (usesTrustedPublishing(rawPublication)) return void 0;
1580
+ if (defaultSecretName !== void 0) return defaultSecretName;
1581
+ if (rawValue === void 0 && !hasResolvedCredential(resolvedPublication, "apiKey")) {
1582
+ return void 0;
1583
+ }
1584
+ addCredentialDiagnostic(
1585
+ state,
1586
+ [...rawPath, "apiKey"],
1587
+ ["targets", index, "output", "publish", "apiKey"],
1588
+ apiKeyOmissionReason(registry)
1589
+ );
1590
+ return void 0;
1591
+ }
1592
+ function apiKeyOmissionReason(registry) {
1593
+ const label = registry === "nuget" ? "NuGet" : "RubyGems";
1594
+ return `Fern ${label} apiKey is not an exact environment expression naming a secret`;
1595
+ }
1596
+ var FERN_DEFAULT_NUGET_API_KEY = "${NUGET_API_TOKEN}";
1597
+ function fernDefaultApiKey(registry, rawOutput, delivery) {
1598
+ if (registry !== "nuget" || delivery !== "github" || rawOutput?.location !== "nuget") {
1599
+ return void 0;
1600
+ }
1601
+ const rawApiKey = rawApiKeyValue(rawOutput);
1602
+ if (typeof rawApiKey === "string" && TRUSTED_PUBLISHING_VALUES.has(rawApiKey)) {
1603
+ return void 0;
1604
+ }
1605
+ return FERN_DEFAULT_NUGET_API_KEY;
1606
+ }
1492
1607
  function mapPypiCredentialExpressions(rawPublication, resolvedPublication, rawPath, state, index) {
1493
1608
  const rawToken = rawPublication?.token;
1494
1609
  if (isEnvironmentExpression(rawToken)) {
@@ -2192,6 +2307,29 @@ var mavenSignatureSchema = zod.z.strictObject({
2192
2307
  "The armored secret key, or an environment expression that resolves to it."
2193
2308
  )
2194
2309
  });
2310
+ function apiKeySecretSchema(registry, example) {
2311
+ return nonEmptyStringSchema.regex(
2312
+ /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/,
2313
+ `${registry} apiKey must be an environment expression naming a secret, such as ${example}`
2314
+ );
2315
+ }
2316
+ function apiKeySecretDescription(registry, example) {
2317
+ 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.`;
2318
+ }
2319
+ function trustedPublishingSchema(registry) {
2320
+ return zod.z.boolean().optional().describe(
2321
+ `Whether ${registry} publication uses trusted publishing (OIDC) instead of an API key secret.`
2322
+ );
2323
+ }
2324
+ function refineApiKeyAgainstTrustedPublishing(publish, context) {
2325
+ if (publish.trustedPublishing === true && publish.apiKey !== void 0) {
2326
+ context.addIssue({
2327
+ code: "custom",
2328
+ message: "apiKey must be omitted when trustedPublishing is true",
2329
+ path: ["apiKey"]
2330
+ });
2331
+ }
2332
+ }
2195
2333
  var commonPublishShape = {
2196
2334
  url: nonEmptyStringSchema.optional().describe("A custom package registry URL."),
2197
2335
  releaseBranch: nonEmptyStringSchema.optional().describe("The branch used by generated package release workflows."),
@@ -2227,12 +2365,16 @@ var sdkConfigV1PublishConfigSchema = zod.z.discriminatedUnion("registry", [
2227
2365
  }),
2228
2366
  zod.z.strictObject({
2229
2367
  registry: zod.z.literal("nuget").describe("Publishes the package to a NuGet-compatible registry."),
2368
+ apiKey: apiKeySecretSchema("NuGet", "${NUGET_API_KEY}").optional().describe(apiKeySecretDescription("NuGet", "${NUGET_API_KEY}")),
2369
+ trustedPublishing: trustedPublishingSchema("NuGet"),
2230
2370
  ...commonPublishShape
2231
- }),
2371
+ }).superRefine(refineApiKeyAgainstTrustedPublishing),
2232
2372
  zod.z.strictObject({
2233
2373
  registry: zod.z.literal("rubygems").describe("Publishes the package to a RubyGems-compatible registry."),
2374
+ apiKey: apiKeySecretSchema("RubyGems", "${RUBYGEMS_API_KEY}").optional().describe(apiKeySecretDescription("RubyGems", "${RUBYGEMS_API_KEY}")),
2375
+ trustedPublishing: trustedPublishingSchema("RubyGems"),
2234
2376
  ...commonPublishShape
2235
- }),
2377
+ }).superRefine(refineApiKeyAgainstTrustedPublishing),
2236
2378
  zod.z.strictObject({
2237
2379
  registry: zod.z.literal("crates").describe("Publishes the crate to a Cargo-compatible registry."),
2238
2380
  token: nonEmptyStringSchema.optional().describe("The Cargo registry token, or an environment expression that resolves to it."),
@@ -2290,7 +2432,7 @@ var sdkConfigV1OutputConfigSchema = zod.z.discriminatedUnion(
2290
2432
  );
2291
2433
  var cliDistributionShape = {
2292
2434
  distribution: cliDistributionConfigSchema.optional().describe(
2293
- "CLI release channels published by the generated release workflow; credentials are GitHub Actions secret names."
2435
+ "CLI release channels and build attestation settings for the generated release workflow; credentials are GitHub Actions secret names."
2294
2436
  )
2295
2437
  };
2296
2438
  function rejectDistributionWithoutGithub(output, context) {
@@ -2336,6 +2478,9 @@ var apiImportSettingsSchema = zod.z.strictObject({
2336
2478
  discriminatedUnionV2: zod.z.boolean().optional().describe("Whether oneOf and anyOf schemas use the newer discriminator inference rules."),
2337
2479
  undiscriminatedUnionsWithLiterals: zod.z.boolean().optional().describe("Whether literal-bearing unions remain undiscriminated during import."),
2338
2480
  inlineAllOfSchemas: zod.z.boolean().optional().describe("Whether schemas composed with allOf are inlined when possible."),
2481
+ inferDiscriminatedUnionBaseProperties: zod.z.boolean().optional().describe(
2482
+ "Whether properties every discriminated-union variant shares are lifted onto the union."
2483
+ ),
2339
2484
  resolveSchemaCollisions: zod.z.boolean().optional().describe("Whether colliding imported schema names are resolved automatically."),
2340
2485
  asyncApiMessageNaming: zod.z.enum(["v1", "v2"]).optional().describe("The naming strategy used for messages imported from AsyncAPI."),
2341
2486
  defaultIntegerFormat: zod.z.enum(["int32", "int64", "uint32", "uint64"]).optional().describe("The integer format used when a schema does not declare one.")
@@ -2434,7 +2579,7 @@ var packageScriptSchema = zod.z.strictObject({
2434
2579
  command: nonEmptyStringSchema.describe("The command executed by the package script.")
2435
2580
  });
2436
2581
  var httpClientSchema = zod.z.strictObject({
2437
- name: zod.z.enum(["axios", "fetch"]).describe("The HTTP client used by generated TypeScript code.")
2582
+ name: zod.z.enum(["axios", "fetch", "node-fetch"]).describe("The HTTP client used by generated TypeScript code.")
2438
2583
  });
2439
2584
  var typescriptGenerationConfigSchema = zod.z.strictObject({
2440
2585
  typescriptVersion: nonEmptyStringSchema.optional().describe("The TypeScript version used by the generated project."),
@@ -2450,6 +2595,37 @@ var typescriptGenerationConfigSchema = zod.z.strictObject({
2450
2595
  /** Name of the top-level namespace export emitted by Fern-compatible TypeScript SDKs. */
2451
2596
  namespaceExportName: nonEmptyStringSchema.optional().describe("The top-level namespace export name."),
2452
2597
  consolidateTypeFiles: zod.z.boolean().optional().describe("Whether generated types are consolidated into fewer source files."),
2598
+ formDataSupport: zod.z.enum(["Node16", "Node18"]).optional().describe(
2599
+ "The multipart form-data implementation: Node16 uses the form-data packages, Node18 uses the platform FormData."
2600
+ ),
2601
+ allowExtraFields: zod.z.boolean().optional().describe(
2602
+ "Whether response parsing keeps unrecognized object keys and accepts unrecognized union members and enum values."
2603
+ ),
2604
+ generateSubpackageExports: zod.z.boolean().optional().describe(
2605
+ "Whether the package manifest exports each subpackage client as its own entry point."
2606
+ ),
2607
+ exportAllRequestsAtRoot: zod.z.boolean().optional().describe("Whether every request type is also exported from the package root."),
2608
+ generateEndpointMetadata: zod.z.boolean().optional().describe("Whether generated clients expose metadata for each endpoint."),
2609
+ outputEsm: zod.z.boolean().optional().describe(
2610
+ "Whether the package is marked as an ES module while still shipping its CommonJS build."
2611
+ ),
2612
+ neverThrowErrors: zod.z.boolean().optional().describe(
2613
+ "Whether client methods return a wrapped response instead of throwing on a non-2xx status."
2614
+ ),
2615
+ includeOtherInUnionTypes: zod.z.boolean().optional().describe("Whether union types gain a member for variants the SDK does not recognize."),
2616
+ includeUtilsOnUnionMembers: zod.z.boolean().optional().describe("Whether helper utilities are generated on each member of a union type."),
2617
+ omitFernHeaders: zod.z.boolean().optional().describe("Whether requests omit the SDK identification headers and the User-Agent header."),
2618
+ linter: zod.z.enum(["biome", "oxlint", "none"]).optional().describe("The linter configured in the generated project."),
2619
+ formatter: zod.z.enum(["prettier", "biome", "oxfmt", "none"]).optional().describe("The formatter configured in the generated project."),
2620
+ exports: jsonObjectSchema.optional().describe(
2621
+ "package.json exports entries, keyed by subpath, applied over the generated exports. Each subpath replaces the generated one."
2622
+ ),
2623
+ files: zod.z.array(nonEmptyStringSchema).optional().describe("package.json files entries, listed ahead of the generated ones."),
2624
+ engines: zod.z.record(nonEmptyStringSchema, nonEmptyStringSchema).optional().describe("package.json engines version ranges, keyed by engine name such as node."),
2625
+ lintStaged: zod.z.record(nonEmptyStringSchema, zod.z.array(nonEmptyStringSchema).min(1)).optional().describe("lint-staged commands to run, keyed by file glob."),
2626
+ browser: zod.z.record(nonEmptyStringSchema, zod.z.union([zod.z.boolean(), nonEmptyStringSchema])).optional().describe(
2627
+ "package.json browser field: a module or file is excluded (false) or replaced by another path."
2628
+ ),
2453
2629
  allowCustomFetcher: zod.z.boolean().optional().describe("Whether callers can supply a custom fetch implementation."),
2454
2630
  useBrandedStringAliases: zod.z.boolean().optional().describe("Whether string aliases are emitted as distinct branded types."),
2455
2631
  useLegacyExports: zod.z.boolean().optional().describe("Whether the package emits the legacy export layout."),
@@ -2523,7 +2699,7 @@ var pythonGenerationConfigSchema = zod.z.strictObject({
2523
2699
  additionalInitExports: zod.z.array(additionalInitExportSchema).optional().describe("Additional names re-exported from the package initializer.")
2524
2700
  });
2525
2701
  var rubyGenerationConfigSchema = zod.z.strictObject({
2526
- requirePaths: zod.z.array(nonEmptyStringSchema).optional().describe("Ruby load paths included in the generated gem specification.")
2702
+ requirePaths: zod.z.array(nonEmptyStringSchema).optional().describe("Files the generated entry file loads with `require`.")
2527
2703
  });
2528
2704
  var rustGenerationConfigSchema = zod.z.strictObject({
2529
2705
  /**
@@ -2865,16 +3041,21 @@ function mapFernConfigToSdkConfigV1(input) {
2865
3041
  ...optional("client", clients.targetOverrides[index]),
2866
3042
  ...optional("docs", docs.targetOverrides[index])
2867
3043
  };
2868
- const targetPackage = { ...invocation.package, ...outputPackage, ...generator.package };
3044
+ const mappedPackage = { ...invocation.package, ...outputPackage, ...generator.package };
2869
3045
  invocation.unsupportedFields.push(
2870
3046
  ...resolveTargetPackage(
2871
- targetPackage,
3047
+ mappedPackage,
2872
3048
  invocation.packageFallbacks,
2873
3049
  generator,
2874
3050
  output.delivery,
2875
3051
  index
2876
3052
  )
2877
3053
  );
3054
+ const targetPackage = withRustCratesPublishDefaults(mappedPackage, {
3055
+ language,
3056
+ output,
3057
+ generator
3058
+ });
2878
3059
  if (Object.keys(targetPackage).length > 0 || outputPackage || generator.package) {
2879
3060
  target.package = targetPackage;
2880
3061
  }
@@ -2943,6 +3124,21 @@ function mapFernConfigToSdkConfigV1(input) {
2943
3124
  ])
2944
3125
  };
2945
3126
  }
3127
+ function withRustCratesPublishDefaults(packageConfig, { language, output, generator }) {
3128
+ if (language !== "rust" || !("publish" in output) || output.publish?.registry !== "crates") {
3129
+ return packageConfig;
3130
+ }
3131
+ const { packageName } = packageConfig;
3132
+ return {
3133
+ ...packageName ? { description: `Rust SDK for ${packageName} generated by Fern` } : {},
3134
+ ...usesCustomLicenseFile(generator) ? {} : { license: { type: "MIT" } },
3135
+ ...packageConfig
3136
+ };
3137
+ }
3138
+ function usesCustomLicenseFile({ config, raw }) {
3139
+ const metadata = isObject(raw) && isObject(raw.metadata) ? raw.metadata : void 0;
3140
+ return isObject(config) && typeof config.packageLicenseFile === "string" || isObject(metadata?.license);
3141
+ }
2946
3142
  function resolveLanguage(generator, index) {
2947
3143
  const language = generator.sdkLanguage ?? generatorLanguages[generator.name];
2948
3144
  if (!language || !isFernLanguage(language)) {
@@ -3081,6 +3277,13 @@ function mapInvocation(generator, language, activeLanguages, index) {
3081
3277
  }
3082
3278
  }
3083
3279
  const docs = mapReadme(generator.readme, language, activeLanguages, state, [...prefix, "readme"]);
3280
+ if (language === "typescript") {
3281
+ const includeApiReference = takeBoolean(config, ["includeApiReference"], state, [
3282
+ ...prefix,
3283
+ "config"
3284
+ ]);
3285
+ if (includeApiReference !== void 0) docs.includeApiReference = includeApiReference;
3286
+ }
3084
3287
  const unsupportedFields = [
3085
3288
  ...state.valueDiagnostics ?? [],
3086
3289
  ...collectUnsupported(config, [...prefix, "config"], state),
@@ -3227,7 +3430,12 @@ function mapCommonConfig(language, config, state, client, generation, packageCon
3227
3430
  }
3228
3431
  const pythonClient = language === "python" ? takeObject(config, ["client"], state, basePath) : void 0;
3229
3432
  const typescriptNaming = language === "typescript" || language === "mcp" ? takeObject(config, ["naming"], state, basePath) : void 0;
3230
- const directClientName = takeString(config, ["clientName", "clientClassName"], state, basePath);
3433
+ const directClientName = takeString(
3434
+ config,
3435
+ language === "ruby" ? ["clientName"] : ["clientName", "clientClassName"],
3436
+ state,
3437
+ basePath
3438
+ );
3231
3439
  const nestedClientName = pythonClient ? takeString(pythonClient.value, ["className"], state, pythonClient.path) : typescriptNaming ? takeString(typescriptNaming.value, ["client"], state, typescriptNaming.path) : language === "ruby" ? (
3232
3440
  // Fern's Ruby generator renames the root client class (`Auth0::Management`) with it.
3233
3441
  takeString(config, ["clientModuleName"], state, basePath)
@@ -3261,7 +3469,13 @@ function mapCommonConfig(language, config, state, client, generation, packageCon
3261
3469
  });
3262
3470
  if (Object.keys(naming).length) generation.naming = naming;
3263
3471
  const wireTests = takeBoolean(config, ["enableWireTests", "generateWireTests"], state, basePath);
3264
- if (wireTests !== void 0) generation.wireTests = { enabled: wireTests };
3472
+ const wireTestsFallback = language === "typescript" ? takeBoolean(config, ["wireTestsFallbackToAutoGeneratedErrorExamples"], state, basePath) : void 0;
3473
+ if (wireTests !== void 0 || wireTestsFallback !== void 0) {
3474
+ generation.wireTests = { enabled: wireTests ?? true };
3475
+ if (wireTestsFallback !== void 0) {
3476
+ generation.wireTests.fallbackToGeneratedErrorExamples = wireTestsFallback;
3477
+ }
3478
+ }
3265
3479
  const webSockets = takeBoolean(
3266
3480
  config,
3267
3481
  ["generateWebSocketClients", "shouldGenerateWebsocketClients", "enableWebsockets"],
@@ -3304,10 +3518,10 @@ function mapTypescriptStreams(config, state, basePath) {
3304
3518
  function mapLanguageConfig(language, config, state, packageConfig, basePath, targetIndex) {
3305
3519
  switch (language) {
3306
3520
  case "typescript":
3307
- return mapTypescript(config, state, packageConfig, basePath);
3521
+ return mapTypescript(config, state, packageConfig, basePath, true);
3308
3522
  case "mcp":
3309
3523
  return {
3310
- ...mapTypescript(config, state, packageConfig, basePath),
3524
+ ...mapTypescript(config, state, packageConfig, basePath, false),
3311
3525
  ...mapMcp(config, state, basePath)
3312
3526
  };
3313
3527
  case "python":
@@ -3333,7 +3547,7 @@ function mapLanguageConfig(language, config, state, packageConfig, basePath, tar
3333
3547
  return mapTerraform(config, state, packageConfig, basePath);
3334
3548
  }
3335
3549
  }
3336
- function mapTypescript(config, state, packageConfig, basePath) {
3550
+ function mapTypescript(config, state, packageConfig, basePath, sdkTarget) {
3337
3551
  const packageJsonManifest = mapPackageManifest(
3338
3552
  config,
3339
3553
  ["packageJson"],
@@ -3346,18 +3560,74 @@ function mapTypescript(config, state, packageConfig, basePath) {
3346
3560
  const packageManagerVersionPart = packageManagerAtIndex >= 0 ? packageJsonPackageManager?.slice(packageManagerAtIndex + 1).split("+")[0] : void 0;
3347
3561
  const packageManagerVersion = packageManagerVersionPart || void 0;
3348
3562
  if (packageJsonManifest) liftPackageJsonDependencies(packageJsonManifest, state, packageConfig);
3563
+ const manifestOptions = sdkTarget && packageJsonManifest ? mapPackageManifestOptions(packageJsonManifest, state, packageConfig) : {};
3349
3564
  const serdeLayer = takeBoolean(config, ["serdeLayer"], state, basePath);
3350
3565
  const noSerdeLayer = takeBoolean(config, ["noSerdeLayer"], state, basePath);
3351
- const fetchSupport = takeEnum(config, ["fetchSupport"], ["native"], state, basePath);
3566
+ const fetchSupport = takeEnum(
3567
+ config,
3568
+ ["fetchSupport"],
3569
+ sdkTarget ? ["native", "node-fetch"] : ["native"],
3570
+ state,
3571
+ basePath
3572
+ );
3352
3573
  const namingObject = takeObject(config, ["naming"], state, basePath);
3353
3574
  const nestedNamespace = namingObject ? takeString(namingObject.value, ["namespace"], state, namingObject.path) : void 0;
3575
+ const sdkOnlyOptions = sdkTarget ? {
3576
+ formDataSupport: takeEnum(
3577
+ config,
3578
+ ["formDataSupport"],
3579
+ ["Node16", "Node18"],
3580
+ state,
3581
+ basePath
3582
+ ),
3583
+ allowExtraFields: takeBoolean(config, ["allowExtraFields"], state, basePath),
3584
+ generateSubpackageExports: takeBoolean(
3585
+ config,
3586
+ ["generateSubpackageExports"],
3587
+ state,
3588
+ basePath
3589
+ ),
3590
+ exportAllRequestsAtRoot: takeBoolean(config, ["exportAllRequestsAtRoot"], state, basePath),
3591
+ generateEndpointMetadata: takeBoolean(
3592
+ config,
3593
+ ["generateEndpointMetadata"],
3594
+ state,
3595
+ basePath
3596
+ ),
3597
+ outputEsm: takeBoolean(config, ["outputEsm"], state, basePath),
3598
+ neverThrowErrors: takeBoolean(config, ["neverThrowErrors"], state, basePath),
3599
+ includeOtherInUnionTypes: takeBoolean(
3600
+ config,
3601
+ ["includeOtherInUnionTypes"],
3602
+ state,
3603
+ basePath
3604
+ ),
3605
+ includeUtilsOnUnionMembers: takeBoolean(
3606
+ config,
3607
+ ["includeUtilsOnUnionMembers"],
3608
+ state,
3609
+ basePath
3610
+ ),
3611
+ omitFernHeaders: takeBoolean(config, ["omitFernHeaders"], state, basePath),
3612
+ linter: takeEnum(config, ["linter"], ["biome", "oxlint", "none"], state, basePath),
3613
+ formatter: takeEnum(
3614
+ config,
3615
+ ["formatter"],
3616
+ ["prettier", "biome", "oxfmt", "none"],
3617
+ state,
3618
+ basePath
3619
+ )
3620
+ } : {};
3354
3621
  return compact({
3622
+ ...sdkOnlyOptions,
3623
+ // Only the manifest writes these keys; nothing in sdkOnlyOptions or below shares a name.
3624
+ ...manifestOptions,
3355
3625
  typescriptVersion: takeString(config, ["typescriptVersion"], state, basePath),
3356
3626
  zodVersion: takeString(config, ["zodVersion"], state, basePath),
3357
3627
  packageManager: takeEnum(config, ["packageManager"], ["pnpm", "yarn"], state, basePath),
3358
3628
  packageManagerVersion,
3359
3629
  testFramework: takeEnum(config, ["testFramework"], ["jest", "vitest"], state, basePath),
3360
- httpClient: fetchSupport === "native" ? { name: "fetch" } : void 0,
3630
+ httpClient: fetchSupport === void 0 ? void 0 : { name: fetchSupport === "native" ? "fetch" : "node-fetch" },
3361
3631
  bundle: takeBoolean(config, ["bundle"], state, basePath),
3362
3632
  exportClassDefault: takeBoolean(config, ["exportClassDefault"], state, basePath),
3363
3633
  namespaceExportName: takeString(config, ["namespaceExportName", "namespaceExport"], state, basePath) ?? nestedNamespace,
@@ -3754,6 +4024,83 @@ function mapPackageManifest(config, aliases, state, packageConfig, basePath) {
3754
4024
  );
3755
4025
  return manifest;
3756
4026
  }
4027
+ function mapPackageManifestOptions(manifest, state, packageConfig) {
4028
+ const bugs = takeManifestBugs(manifest, state);
4029
+ if (bugs) packageConfig.bugs = bugs;
4030
+ const repository = takeManifestRepository(manifest, state);
4031
+ if (repository !== void 0 && packageConfig.repository === void 0) {
4032
+ packageConfig.repository = repository;
4033
+ }
4034
+ const exports = takeObject(manifest.value, ["exports"], state, manifest.path);
4035
+ if (exports) consume(state, exports.path);
4036
+ return compact({
4037
+ exports: exports?.value,
4038
+ scripts: takeManifestScripts(manifest, state),
4039
+ files: takeStringArray(manifest.value, ["files"], state, manifest.path),
4040
+ engines: takeManifestStringRecord(manifest, "engines", state),
4041
+ lintStaged: takeManifestLintStaged(manifest, state),
4042
+ browser: takeManifestBrowser(manifest, state)
4043
+ });
4044
+ }
4045
+ function takeManifestBugs(manifest, state) {
4046
+ const text = takeString(manifest.value, ["bugs"], state, manifest.path);
4047
+ if (text !== void 0) return { url: text };
4048
+ const located = takeObject(manifest.value, ["bugs"], state, manifest.path);
4049
+ if (!located) return void 0;
4050
+ const url = takeString(located.value, ["url"], state, located.path);
4051
+ const email = takeString(located.value, ["email"], state, located.path);
4052
+ return url === void 0 && email === void 0 ? void 0 : compact({ url, email });
4053
+ }
4054
+ function takeManifestRepository(manifest, state) {
4055
+ const text = takeString(manifest.value, ["repository"], state, manifest.path);
4056
+ if (text !== void 0) return text;
4057
+ const located = takeObject(manifest.value, ["repository"], state, manifest.path);
4058
+ if (!located) return void 0;
4059
+ const url = takeString(located.value, ["url"], state, located.path);
4060
+ if (url !== void 0) void takeEnum(located.value, ["type"], ["git"], state, located.path);
4061
+ return url;
4062
+ }
4063
+ function takeManifestScripts(manifest, state) {
4064
+ const located = takeObject(manifest.value, ["scripts"], state, manifest.path);
4065
+ if (!located) return void 0;
4066
+ const scripts = Object.entries(located.value).flatMap(([name, command]) => {
4067
+ if (typeof command !== "string" || !command) return [];
4068
+ consume(state, [...located.path, name]);
4069
+ return [{ name, command }];
4070
+ });
4071
+ return scripts.length ? scripts : void 0;
4072
+ }
4073
+ function takeManifestStringRecord(manifest, key, state) {
4074
+ const located = takeObject(manifest.value, [key], state, manifest.path);
4075
+ if (!located) return void 0;
4076
+ const entries = Object.entries(located.value).flatMap(([name, value]) => {
4077
+ if (typeof value !== "string" || !value) return [];
4078
+ consume(state, [...located.path, name]);
4079
+ return [[name, value]];
4080
+ });
4081
+ return entries.length ? Object.fromEntries(entries) : void 0;
4082
+ }
4083
+ function takeManifestLintStaged(manifest, state) {
4084
+ const located = takeObject(manifest.value, ["lint-staged"], state, manifest.path);
4085
+ if (!located) return void 0;
4086
+ const entries = Object.entries(located.value).flatMap(([glob, commands]) => {
4087
+ const list = typeof commands === "string" ? [commands] : stringArrayValue(commands);
4088
+ if (!list?.length || list.some((command) => !command)) return [];
4089
+ consume(state, [...located.path, glob]);
4090
+ return [[glob, list]];
4091
+ });
4092
+ return entries.length ? Object.fromEntries(entries) : void 0;
4093
+ }
4094
+ function takeManifestBrowser(manifest, state) {
4095
+ const located = takeObject(manifest.value, ["browser"], state, manifest.path);
4096
+ if (!located) return void 0;
4097
+ const entries = Object.entries(located.value).flatMap(([name, value]) => {
4098
+ if (typeof value !== "boolean" && (typeof value !== "string" || !value)) return [];
4099
+ consume(state, [...located.path, name]);
4100
+ return [[name, value]];
4101
+ });
4102
+ return entries.length ? Object.fromEntries(entries) : void 0;
4103
+ }
3757
4104
  function takeManifestAuthors(manifest, state) {
3758
4105
  const located = locate(manifest.value, ["authors"], manifest.path);
3759
4106
  if (!located || !Array.isArray(located.value)) return [];