@postman/sdk-config 0.5.0 → 0.6.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.
package/README.md CHANGED
@@ -81,11 +81,26 @@ a dedicated secret channel and remove them from generation payloads, logs, and s
81
81
  Fern migration preserves a credential only when the raw `generators.yml` value is exactly an
82
82
  environment expression; normalized, resolved, or literal values are omitted with a warning.
83
83
 
84
+ Service-to-service consumers that must remain compatible with additive V1 fields can use
85
+ `parseSdkConfigV1ForConsumption`. It ignores unknown object properties recursively while retaining
86
+ all validation for known fields. Customer-authored configuration should continue to use the strict
87
+ parser or validator so misspellings are reported.
88
+
84
89
  `SdkConfigV1` accepts customer-facing local source paths and HTTP(S) source URLs, but not
85
90
  server-owned signed URLs or artifact metadata. See the
86
91
  [SDK Config v1 reference](src/sdk-config/v1/README.md) for source materialization and target
87
92
  precedence rules.
88
93
 
94
+ ## Version 1 evolution policy
95
+
96
+ SDK Config V1 and SDK Config IR V1 evolve additively. New properties must be optional. Existing
97
+ properties cannot be removed, renamed, narrowed, or made required; those changes require a new
98
+ schema version. Adding an enum or union member is also not automatically forward-compatible because
99
+ an older consumer cannot ignore an unfamiliar value.
100
+
101
+ Schema contract fingerprints make every V1 shape change visible in review. Do not update those
102
+ fingerprints for a removal, rename, narrower type, or newly required property under V1.
103
+
89
104
  ## Use SDK Config in an SDK Generation API request
90
105
 
91
106
  An SDK Generation API request combines three kinds of data:
package/dist/index.cjs CHANGED
@@ -1246,10 +1246,16 @@ function finishOutputMapping(mapping, outputMode, sourcePath, state, index) {
1246
1246
  for (const path of collectLeafPaths(outputMode, sourcePath)) {
1247
1247
  if (path[path.length - 1] === "_visit") consume(state, path);
1248
1248
  }
1249
+ const legacyGenerateFullProject = isObject(outputMode) ? outputMode.generateFullProject : void 0;
1250
+ const generationShape = typeof legacyGenerateFullProject === "boolean" ? { generation: { ...mapping.generation, generateFullProject: legacyGenerateFullProject } } : {};
1251
+ if (typeof legacyGenerateFullProject === "boolean") {
1252
+ consume(state, [...sourcePath, "generateFullProject"]);
1253
+ }
1249
1254
  const unsupported = collectUnsupported(outputMode, sourcePath, state);
1250
1255
  const hasCredentials = unsupported.some((diagnostic) => isCredentialPath(diagnostic.path));
1251
1256
  return {
1252
1257
  ...mapping,
1258
+ ...generationShape,
1253
1259
  unsupportedFields: [
1254
1260
  ...state.credentialDiagnostics ?? [],
1255
1261
  ...hasCredentials ? [
@@ -1333,6 +1339,125 @@ function outputDiagnostic(path, index) {
1333
1339
  suggestedAction: resolvedGuidance?.suggestedAction ?? "Review this output setting and preserve it outside SDK Config when no equivalent exists."
1334
1340
  };
1335
1341
  }
1342
+ var OMIT = /* @__PURE__ */ Symbol("omit");
1343
+ function inputJsonSchema(schema) {
1344
+ return zod.z.toJSONSchema(schema, {
1345
+ io: "input",
1346
+ unrepresentable: "any"
1347
+ });
1348
+ }
1349
+ function stripUnknownProperties(value, schema) {
1350
+ return project(value, schema, schema, false);
1351
+ }
1352
+ function omitSchemaDefaults(value, schema) {
1353
+ const projected = project(value, schema, schema, true);
1354
+ return projected === OMIT ? void 0 : projected;
1355
+ }
1356
+ function project(value, candidateSchema, rootSchema, omitDefaults) {
1357
+ const resolvedCandidate = resolveSchema(candidateSchema, rootSchema);
1358
+ if (omitDefaults && Object.hasOwn(resolvedCandidate, "default") && deepEqual(value, resolvedCandidate.default)) {
1359
+ return OMIT;
1360
+ }
1361
+ const schema = selectSchema(value, resolvedCandidate, rootSchema);
1362
+ if (omitDefaults && schema !== resolvedCandidate && Object.hasOwn(schema, "default") && deepEqual(value, schema.default)) {
1363
+ return OMIT;
1364
+ }
1365
+ if (Array.isArray(value)) {
1366
+ if (!schema.items) return value;
1367
+ return value.map((item) => {
1368
+ const projected = project(item, schema.items, rootSchema, omitDefaults);
1369
+ return projected === OMIT ? item : projected;
1370
+ });
1371
+ }
1372
+ if (!isRecord(value) || schema.type !== "object") return value;
1373
+ const entries = [];
1374
+ const properties = schema.properties ?? {};
1375
+ const unionProperties = omitDefaults ? properties : collectAlternativeProperties(resolvedCandidate, rootSchema);
1376
+ for (const [key, propertySchema] of Object.entries(properties)) {
1377
+ if (!Object.hasOwn(value, key)) continue;
1378
+ const projected = project(value[key], propertySchema, rootSchema, omitDefaults);
1379
+ if (projected !== OMIT) entries.push([key, projected]);
1380
+ }
1381
+ for (const [key, fieldValue] of Object.entries(value)) {
1382
+ if (Object.hasOwn(properties, key)) continue;
1383
+ if (Object.hasOwn(unionProperties, key)) {
1384
+ entries.push([key, fieldValue]);
1385
+ continue;
1386
+ }
1387
+ if (schema.additionalProperties !== false) {
1388
+ const projected = typeof schema.additionalProperties === "object" ? project(fieldValue, schema.additionalProperties, rootSchema, omitDefaults) : fieldValue;
1389
+ if (projected !== OMIT) entries.push([key, projected]);
1390
+ } else if (omitDefaults) {
1391
+ throw new Error(`Cannot serialize property unknown to the wire schema: ${key}`);
1392
+ }
1393
+ }
1394
+ return Object.fromEntries(entries);
1395
+ }
1396
+ function collectAlternativeProperties(schema, rootSchema) {
1397
+ const alternatives = schema.oneOf ?? schema.anyOf;
1398
+ if (!alternatives) return schema.properties ?? {};
1399
+ return Object.assign(
1400
+ {},
1401
+ ...alternatives.map((alternative) => resolveSchema(alternative, rootSchema).properties ?? {})
1402
+ );
1403
+ }
1404
+ function selectSchema(value, schema, rootSchema) {
1405
+ const alternatives = schema.oneOf ?? schema.anyOf;
1406
+ if (!alternatives || alternatives.length === 0) return schema;
1407
+ const best = alternatives.map((alternative, index) => {
1408
+ const resolved = resolveSchema(alternative, rootSchema);
1409
+ return { index, schema: resolved, score: schemaMatchScore(value, resolved) };
1410
+ }).sort((left, right) => right.score - left.score || left.index - right.index)[0];
1411
+ return best.score === Number.NEGATIVE_INFINITY ? schema : best.schema;
1412
+ }
1413
+ function schemaMatchScore(value, schema) {
1414
+ if (Object.hasOwn(schema, "const") && !deepEqual(value, schema.const)) {
1415
+ return Number.NEGATIVE_INFINITY;
1416
+ }
1417
+ if (schema.enum && !schema.enum.some((candidate) => deepEqual(value, candidate))) {
1418
+ return Number.NEGATIVE_INFINITY;
1419
+ }
1420
+ if (schema.type === "object") {
1421
+ if (!isRecord(value)) return Number.NEGATIVE_INFINITY;
1422
+ const properties = schema.properties ?? {};
1423
+ for (const [key, propertySchema] of Object.entries(properties)) {
1424
+ if (!Object.hasOwn(value, key)) continue;
1425
+ if (Object.hasOwn(propertySchema, "const") && !deepEqual(value[key], propertySchema.const)) {
1426
+ return Number.NEGATIVE_INFINITY;
1427
+ }
1428
+ }
1429
+ return Object.keys(value).filter((key) => Object.hasOwn(properties, key)).length;
1430
+ }
1431
+ if (schema.type === "array") return Array.isArray(value) ? 1 : Number.NEGATIVE_INFINITY;
1432
+ if (schema.type === "string") return typeof value === "string" ? 1 : Number.NEGATIVE_INFINITY;
1433
+ if (schema.type === "number" || schema.type === "integer") {
1434
+ return typeof value === "number" ? 1 : Number.NEGATIVE_INFINITY;
1435
+ }
1436
+ if (schema.type === "boolean") return typeof value === "boolean" ? 1 : Number.NEGATIVE_INFINITY;
1437
+ if (schema.type === "null") return value === null ? 1 : Number.NEGATIVE_INFINITY;
1438
+ return 0;
1439
+ }
1440
+ function resolveSchema(schema, rootSchema) {
1441
+ if (!schema.$ref) return schema;
1442
+ if (!schema.$ref.startsWith("#/$defs/")) return schema;
1443
+ const name = schema.$ref.slice("#/$defs/".length).replaceAll("~1", "/").replaceAll("~0", "~");
1444
+ return rootSchema.$defs?.[name] ?? schema;
1445
+ }
1446
+ function isRecord(value) {
1447
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1448
+ }
1449
+ function deepEqual(left, right) {
1450
+ if (Object.is(left, right)) return true;
1451
+ if (Array.isArray(left) && Array.isArray(right)) {
1452
+ return left.length === right.length && left.every((value, index) => deepEqual(value, right[index]));
1453
+ }
1454
+ if (isRecord(left) && isRecord(right)) {
1455
+ const leftKeys = Object.keys(left);
1456
+ const rightKeys = Object.keys(right);
1457
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.hasOwn(right, key) && deepEqual(left[key], right[key]));
1458
+ }
1459
+ return false;
1460
+ }
1336
1461
  var namingConfigSchema = zod.z.strictObject({
1337
1462
  clientName: nonEmptyStringSchema.optional(),
1338
1463
  exportedClientName: nonEmptyStringSchema.optional(),
@@ -1431,6 +1556,8 @@ var generationConfigOverrideShape = {
1431
1556
  inferServiceNames: zod.z.boolean().optional(),
1432
1557
  includeDeprecatedOperations: zod.z.boolean().optional(),
1433
1558
  multipleResponses: zod.z.boolean().optional(),
1559
+ /** Generate the entire SDK project, including package manifests and publishable artifacts. */
1560
+ generateFullProject: zod.z.boolean().optional(),
1434
1561
  devContainer: zod.z.boolean().optional(),
1435
1562
  allowMockClient: zod.z.boolean().optional(),
1436
1563
  ignoreFiles: zod.z.array(nonEmptyStringSchema).optional(),
@@ -1459,6 +1586,7 @@ var sdkConfigV1GenerationConfigSchema = zod.z.strictObject({
1459
1586
  inferServiceNames: zod.z.boolean().default(false),
1460
1587
  includeDeprecatedOperations: zod.z.boolean().default(true),
1461
1588
  multipleResponses: zod.z.boolean().default(false),
1589
+ generateFullProject: zod.z.boolean().default(false),
1462
1590
  devContainer: zod.z.boolean().default(false),
1463
1591
  allowMockClient: zod.z.boolean().default(false)
1464
1592
  });
@@ -2008,6 +2136,10 @@ function validateSdkConfigV1(value) {
2008
2136
  function parseSdkConfigV1(value) {
2009
2137
  return sdkConfigV1Schema.parse(value);
2010
2138
  }
2139
+ var sdkConfigV1InputJsonSchema = inputJsonSchema(sdkConfigV1Schema);
2140
+ function parseSdkConfigV1ForConsumption(value) {
2141
+ return parseSdkConfigV1(stripUnknownProperties(value, sdkConfigV1InputJsonSchema));
2142
+ }
2011
2143
 
2012
2144
  // src/sdk-config/v1/fern-config-mapper.ts
2013
2145
  var generatorLanguages = {
@@ -2063,7 +2195,14 @@ function mapFernConfigToSdkConfigV1(input) {
2063
2195
  api.audiences = [...input.group.audiences.audiences];
2064
2196
  }
2065
2197
  const targets = mapped.map(
2066
- ({ generator, invocation, language, output, package: outputPackage }, index) => {
2198
+ ({
2199
+ generator,
2200
+ invocation,
2201
+ language,
2202
+ output,
2203
+ package: outputPackage,
2204
+ generation: outputGeneration
2205
+ }, index) => {
2067
2206
  const target = {
2068
2207
  language,
2069
2208
  output,
@@ -2077,6 +2216,7 @@ function mapFernConfigToSdkConfigV1(input) {
2077
2216
  }
2078
2217
  const targetGeneration = {
2079
2218
  ...generations.targetOverrides[index],
2219
+ ...outputGeneration,
2080
2220
  ...invocation.targetGeneration
2081
2221
  };
2082
2222
  if (Object.keys(targetGeneration).length > 0) {
@@ -2254,6 +2394,9 @@ function mapInvocation(generator, language, activeLanguages, index) {
2254
2394
  if (generator.keywords?.length) {
2255
2395
  generation.reservedKeywords = [...generator.keywords];
2256
2396
  }
2397
+ if (generator.generateFullProject !== void 0) {
2398
+ generation.generateFullProject = generator.generateFullProject;
2399
+ }
2257
2400
  const settings = isObject(generator.settings) ? generator.settings : void 0;
2258
2401
  if (settings) {
2259
2402
  const inlinePathParameters = takeBoolean(settings, ["inlinePathParameters"], state, [
@@ -2396,6 +2539,8 @@ function mapCommonConfig(language, config, state, client, generation, packageCon
2396
2539
  basePath
2397
2540
  );
2398
2541
  if (webSockets !== void 0) generation.webSockets = webSockets;
2542
+ const generateFullProject = takeBoolean(config, ["generateFullProject"], state, basePath);
2543
+ if (generateFullProject !== void 0) generation.generateFullProject = generateFullProject;
2399
2544
  const inlineTypes = takeBoolean(config, ["enableInlineTypes"], state, basePath);
2400
2545
  const omitUndefined = takeBoolean(config, ["omitUndefined"], state, basePath);
2401
2546
  const forwardEnums = takeBoolean(config, ["enableForwardCompatibleEnums"], state, basePath);
@@ -3000,6 +3145,8 @@ var generationConfigSchema = zod.z.strictObject({
3000
3145
  inferServiceNames: zod.z.boolean().default(false),
3001
3146
  includeDeprecatedOperations: zod.z.boolean().default(true),
3002
3147
  multipleResponses: zod.z.boolean().default(false),
3148
+ /** Generate the entire SDK project, including package manifests and publishable artifacts. */
3149
+ generateFullProject: zod.z.boolean().default(false),
3003
3150
  devContainer: zod.z.boolean().default(false),
3004
3151
  allowMockClient: zod.z.boolean().default(false),
3005
3152
  ignoreFiles: zod.z.array(nonEmptyStringSchema).optional(),
@@ -3399,6 +3546,30 @@ var sdkConfigIrV1Schema = zod.z.strictObject({
3399
3546
  function parseSdkConfigIrV1(value) {
3400
3547
  return sdkConfigIrV1Schema.parse(value);
3401
3548
  }
3549
+ var sdkConfigIrCapabilitySchema = zod.z.enum(["generation.generateFullProject"]);
3550
+ var sdkConfigIrV1InputJsonSchema = inputJsonSchema(sdkConfigIrV1Schema);
3551
+ function serializeSdkConfigIrV1(value) {
3552
+ const parsed = parseSdkConfigIrV1(value);
3553
+ const wireValue = omitSchemaDefaults(parsed, sdkConfigIrV1InputJsonSchema);
3554
+ const requiredCapabilities = [];
3555
+ if (wireValue === void 0) {
3556
+ throw new Error("SDK Config IR v1 root cannot be omitted from its wire payload");
3557
+ }
3558
+ if (parsed.generation.generateFullProject) {
3559
+ requiredCapabilities.push("generation.generateFullProject");
3560
+ }
3561
+ const json = JSON.stringify(wireValue);
3562
+ if (json === void 0) {
3563
+ throw new Error("SDK Config IR v1 could not be serialized as JSON");
3564
+ }
3565
+ return {
3566
+ body: new TextEncoder().encode(json),
3567
+ requiredCapabilities
3568
+ };
3569
+ }
3570
+ function parseSdkConfigIrV1ForConsumption(value) {
3571
+ return parseSdkConfigIrV1(stripUnknownProperties(value, sdkConfigIrV1InputJsonSchema));
3572
+ }
3402
3573
 
3403
3574
  exports.FernConfigMappingError = FernConfigMappingError;
3404
3575
  exports.SDK_CONFIG_IR_V1_SCHEMA_VERSION = SDK_CONFIG_IR_V1_SCHEMA_VERSION;
@@ -3433,7 +3604,9 @@ exports.nonEmptyStringSchema = nonEmptyStringSchema;
3433
3604
  exports.outputConfigSchema = outputConfigSchema;
3434
3605
  exports.packageConfigSchema = packageConfigSchema;
3435
3606
  exports.parseSdkConfigIrV1 = parseSdkConfigIrV1;
3607
+ exports.parseSdkConfigIrV1ForConsumption = parseSdkConfigIrV1ForConsumption;
3436
3608
  exports.parseSdkConfigV1 = parseSdkConfigV1;
3609
+ exports.parseSdkConfigV1ForConsumption = parseSdkConfigV1ForConsumption;
3437
3610
  exports.phpGenerationConfigSchema = phpGenerationConfigSchema;
3438
3611
  exports.publishConfigSchema = publishConfigSchema;
3439
3612
  exports.publishRegistrySchema = publishRegistrySchema;
@@ -3444,6 +3617,7 @@ exports.relativePathSchema = relativePathSchema;
3444
3617
  exports.replayConfigSchema = replayConfigSchema;
3445
3618
  exports.rubyGenerationConfigSchema = rubyGenerationConfigSchema;
3446
3619
  exports.rustGenerationConfigSchema = rustGenerationConfigSchema;
3620
+ exports.sdkConfigIrCapabilitySchema = sdkConfigIrCapabilitySchema;
3447
3621
  exports.sdkConfigIrV1Schema = sdkConfigIrV1Schema;
3448
3622
  exports.sdkConfigV1ApiConfigSchema = sdkConfigV1ApiConfigSchema;
3449
3623
  exports.sdkConfigV1AuthConfigSchema = sdkConfigV1AuthConfigSchema;
@@ -3484,6 +3658,7 @@ exports.sdkConfigV1TargetSchema = sdkConfigV1TargetSchema;
3484
3658
  exports.sdkConfigV1TerraformGenerationConfigSchema = terraformGenerationConfigSchema;
3485
3659
  exports.sdkConfigV1TypescriptGenerationConfigSchema = typescriptGenerationConfigSchema;
3486
3660
  exports.sdkConfigV1WebhookSignatureSchema = sdkConfigV1WebhookSignatureSchema;
3661
+ exports.serializeSdkConfigIrV1 = serializeSdkConfigIrV1;
3487
3662
  exports.sourceConfigSchema = sourceConfigSchema;
3488
3663
  exports.sourceSpecConfigSchema = sourceSpecConfigSchema;
3489
3664
  exports.sourceSpecTypeSchema = sourceSpecTypeSchema;