@workos/oagen 0.6.0 → 0.7.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.
@@ -550,7 +550,7 @@ function findSchemaByName(pascalName, schemas) {
550
550
  for (const [k, v] of Object.entries(schemas)) if (resolveSchemaName(k) === pascalName) return v;
551
551
  return null;
552
552
  }
553
- /** Walk into a schema to find a field's sub-schema, resolving allOf. */
553
+ /** Walk into a schema to find a field's sub-schema, resolving allOf and oneOf/anyOf. */
554
554
  function findNestedFieldSchema(fieldName, parentSchema, schemas) {
555
555
  if (parentSchema.properties?.[fieldName]) return parentSchema.properties[fieldName];
556
556
  if (parentSchema.allOf) for (const sub of parentSchema.allOf) {
@@ -562,6 +562,15 @@ function findNestedFieldSchema(fieldName, parentSchema, schemas) {
562
562
  }
563
563
  if (resolved.properties?.[fieldName]) return resolved.properties[fieldName];
564
564
  }
565
+ for (const variant of [...parentSchema.oneOf ?? [], ...parentSchema.anyOf ?? []]) {
566
+ let resolved = variant;
567
+ if (variant.$ref) {
568
+ const segments = variant.$ref.split("/");
569
+ const refName = segments[segments.length - 1];
570
+ if (refName && schemas[refName]) resolved = schemas[refName];
571
+ }
572
+ if (resolved.properties?.[fieldName]) return resolved.properties[fieldName];
573
+ }
565
574
  return null;
566
575
  }
567
576
  /** Extract a model (and nested models) from an inline field schema. */
@@ -727,6 +736,19 @@ function extractModel(name, schema, schemas) {
727
736
  if (!fieldSchema) continue;
728
737
  fields.push(buildFieldFromSchema(fieldName, fieldSchema, name, requiredSet));
729
738
  }
739
+ if (fields.length === 0 && (schema.oneOf || schema.anyOf)) {
740
+ const variants = schema.oneOf ?? schema.anyOf ?? [];
741
+ const emptyRequired = /* @__PURE__ */ new Set();
742
+ const seenFieldNames = /* @__PURE__ */ new Set();
743
+ for (const variant of variants) {
744
+ if (!variant.properties) continue;
745
+ for (const [fieldName, fieldSchema] of Object.entries(variant.properties)) {
746
+ if (!fieldSchema || seenFieldNames.has(fieldName)) continue;
747
+ seenFieldNames.add(fieldName);
748
+ fields.push(buildFieldFromSchema(fieldName, fieldSchema, name, emptyRequired));
749
+ }
750
+ }
751
+ }
730
752
  if (schema.additionalProperties && typeof schema.additionalProperties === "object" && schema.properties) {
731
753
  if (Object.keys(schema.additionalProperties).length > 0) {
732
754
  const valueType = schemaToTypeRef(schema.additionalProperties, "additionalProperties", name);
@@ -766,6 +788,18 @@ function extractAllOfModel(name, schema, schemas) {
766
788
  });
767
789
  if (f.required) requiredSet.add(f.name);
768
790
  }
791
+ } else if (resolved.oneOf || resolved.anyOf) {
792
+ const variants = resolved.oneOf ?? resolved.anyOf ?? [];
793
+ const emptyRequired = /* @__PURE__ */ new Set();
794
+ const seenFieldNames = new Set(fields.map((f) => f.name));
795
+ for (const variant of variants) {
796
+ if (!variant.properties) continue;
797
+ for (const [fieldName, fieldSchema] of Object.entries(variant.properties)) {
798
+ if (!fieldSchema || seenFieldNames.has(fieldName)) continue;
799
+ seenFieldNames.add(fieldName);
800
+ fields.push(buildFieldFromSchema(fieldName, fieldSchema, name, emptyRequired));
801
+ }
802
+ }
769
803
  } else {
770
804
  if (resolved.required) for (const r of resolved.required) requiredSet.add(r);
771
805
  if (resolved.properties) {
@@ -1622,6 +1656,94 @@ function inferDeeperContext(path) {
1622
1656
  if (nonParams.length >= 3) return toPascalCase(nonParams.slice(1, -1).join("_"));
1623
1657
  return null;
1624
1658
  }
1659
+ /**
1660
+ * Extract mutually-exclusive parameter groups from the `x-mutually-exclusive-parameter-groups`
1661
+ * operation extension. Cross-references grouped parameter names against the already-extracted
1662
+ * IR parameter arrays so emitters get object-identity references.
1663
+ */
1664
+ function extractParameterGroups(op, allIRParams, operationContext) {
1665
+ const raw = op["x-mutually-exclusive-parameter-groups"];
1666
+ if (!raw || typeof raw !== "object") return void 0;
1667
+ const paramByName = /* @__PURE__ */ new Map();
1668
+ for (const p of allIRParams) paramByName.set(p.name, p);
1669
+ const groups = [];
1670
+ for (const [groupName, groupDef] of Object.entries(raw)) {
1671
+ if (!groupDef || typeof groupDef !== "object") throw new Error(`Malformed x-mutually-exclusive-parameter-groups.${groupName} in ${operationContext}: expected an object with "optional" and "variants".`);
1672
+ if (typeof groupDef.optional !== "boolean") throw new Error(`Malformed x-mutually-exclusive-parameter-groups.${groupName}.optional in ${operationContext}: expected a boolean.`);
1673
+ if (!groupDef.variants || typeof groupDef.variants !== "object") throw new Error(`Malformed x-mutually-exclusive-parameter-groups.${groupName}.variants in ${operationContext}: expected an object mapping variant names to parameter name arrays.`);
1674
+ if (Object.keys(groupDef.variants).length === 0) throw new Error(`Malformed x-mutually-exclusive-parameter-groups.${groupName} in ${operationContext}: group has zero variants.`);
1675
+ const variants = Object.entries(groupDef.variants).map(([variantName, paramNames]) => {
1676
+ if (!Array.isArray(paramNames) || paramNames.length === 0) throw new Error(`Malformed x-mutually-exclusive-parameter-groups.${groupName}.variants.${variantName} in ${operationContext}: expected a non-empty array of parameter names.`);
1677
+ return {
1678
+ name: variantName,
1679
+ parameters: paramNames.map((pName) => {
1680
+ const irParam = paramByName.get(pName);
1681
+ if (!irParam) throw new Error(`x-mutually-exclusive-parameter-groups.${groupName}.variants.${variantName} references parameter "${pName}" which does not exist in the operation's parameters[] (${operationContext}).`);
1682
+ return irParam;
1683
+ })
1684
+ };
1685
+ });
1686
+ groups.push({
1687
+ name: groupName,
1688
+ optional: groupDef.optional,
1689
+ variants
1690
+ });
1691
+ }
1692
+ return groups.length > 0 ? groups : void 0;
1693
+ }
1694
+ /**
1695
+ * Extract mutually-exclusive body parameter groups from the
1696
+ * `x-mutually-exclusive-body-groups` operation extension. Unlike query
1697
+ * parameter groups (whose parameters live in operation.queryParams), body
1698
+ * group parameters are synthetic — built from the oneOf variant schemas in
1699
+ * the request body. Emitters use the group's presence to generate sum-type
1700
+ * interfaces and custom JSON marshalling instead of flat optional fields.
1701
+ */
1702
+ function extractBodyParameterGroups(op, operationContext) {
1703
+ const raw = op["x-mutually-exclusive-body-groups"];
1704
+ if (!raw || typeof raw !== "object") return void 0;
1705
+ const bodySchema = op.requestBody?.content?.["application/json"]?.schema;
1706
+ const variantFieldSchemas = /* @__PURE__ */ new Map();
1707
+ if (bodySchema) {
1708
+ for (const sub of bodySchema.allOf ?? []) for (const variant of sub.oneOf ?? []) if (variant.properties) {
1709
+ for (const [name, fieldSchema] of Object.entries(variant.properties)) if (fieldSchema) variantFieldSchemas.set(name, fieldSchema);
1710
+ }
1711
+ for (const variant of bodySchema.oneOf ?? []) if (variant.properties) {
1712
+ for (const [name, fieldSchema] of Object.entries(variant.properties)) if (fieldSchema) variantFieldSchemas.set(name, fieldSchema);
1713
+ }
1714
+ }
1715
+ const groups = [];
1716
+ for (const [groupName, groupDef] of Object.entries(raw)) {
1717
+ if (!groupDef || typeof groupDef !== "object") continue;
1718
+ if (typeof groupDef.optional !== "boolean") continue;
1719
+ if (!groupDef.variants || typeof groupDef.variants !== "object") continue;
1720
+ if (Object.keys(groupDef.variants).length === 0) continue;
1721
+ const variants = Object.entries(groupDef.variants).map(([variantName, paramNames]) => {
1722
+ if (!Array.isArray(paramNames) || paramNames.length === 0) throw new Error(`Malformed x-mutually-exclusive-body-groups.${groupName}.variants.${variantName} in ${operationContext}: expected a non-empty array of parameter names.`);
1723
+ return {
1724
+ name: variantName,
1725
+ parameters: paramNames.map((pName) => {
1726
+ const fieldSchema = variantFieldSchemas.get(pName);
1727
+ return {
1728
+ name: pName,
1729
+ type: fieldSchema ? schemaToTypeRef(fieldSchema, toPascalCase(pName)) : {
1730
+ kind: "primitive",
1731
+ type: "string"
1732
+ },
1733
+ required: false,
1734
+ description: fieldSchema?.description
1735
+ };
1736
+ })
1737
+ };
1738
+ });
1739
+ groups.push({
1740
+ name: groupName,
1741
+ optional: groupDef.optional,
1742
+ variants
1743
+ });
1744
+ }
1745
+ return groups.length > 0 ? groups : void 0;
1746
+ }
1625
1747
  function buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName) {
1626
1748
  const allParams = [...pathLevelParams, ...op.parameters ?? []];
1627
1749
  const hasIdempotencyHeader = allParams.some((p) => p.in === "header" && p.name.toLowerCase() === "idempotency-key");
@@ -1647,6 +1769,16 @@ function buildOperation(method, path, op, pathLevelParams, operationIdTransform,
1647
1769
  };
1648
1770
  } else if (paginationFromParams) pagination = paginationFromParams;
1649
1771
  const security = extractOperationSecurity(op.security);
1772
+ const allIRParams = [
1773
+ ...pathParams,
1774
+ ...queryParams,
1775
+ ...headerParams,
1776
+ ...cookieParams
1777
+ ];
1778
+ const opLabel = op.operationId ?? `${method.toUpperCase()} ${path}`;
1779
+ const queryParamGroups = extractParameterGroups(op, allIRParams, opLabel);
1780
+ const bodyParamGroups = extractBodyParameterGroups(op, opLabel);
1781
+ const parameterGroups = queryParamGroups || bodyParamGroups ? [...queryParamGroups ?? [], ...bodyParamGroups ?? []] : void 0;
1650
1782
  return {
1651
1783
  operation: {
1652
1784
  name: inferOperationName(method, path, op.operationId, operationIdTransform),
@@ -1666,7 +1798,8 @@ function buildOperation(method, path, op, pathLevelParams, operationIdTransform,
1666
1798
  injectIdempotencyKey: hasIdempotencyHeader,
1667
1799
  deprecated: op.deprecated || void 0,
1668
1800
  async: op["x-oagen-async"],
1669
- security
1801
+ security,
1802
+ parameterGroups
1670
1803
  },
1671
1804
  inlineModels
1672
1805
  };