@workos/oagen 0.5.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.
- package/dist/cli/index.mjs +580 -34
- package/dist/cli/index.mjs.map +1 -1
- package/dist/compat/index.d.mts +1 -1
- package/dist/compat/index.mjs +21 -3
- package/dist/compat/index.mjs.map +1 -1
- package/dist/{diff-CSy3g-Bq.mjs → diff-BYadQQV7.mjs} +353 -30
- package/dist/diff-BYadQQV7.mjs.map +1 -0
- package/dist/index.d.mts +42 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +227 -4
- package/dist/index.mjs.map +1 -1
- package/dist/scripts/smoke/baseline.mjs +225 -3
- package/dist/scripts/smoke/baseline.mjs.map +1 -1
- package/dist/smoke.d.mts +33 -0
- package/dist/smoke.d.mts.map +1 -1
- package/dist/smoke.mjs +225 -3
- package/dist/smoke.mjs.map +1 -1
- package/dist/tree-sitter-C53w_0WE.mjs.map +1 -1
- package/dist/{types-D84LjrVG.d.mts → types-DJMbCtIO.d.mts} +40 -2
- package/dist/types-DJMbCtIO.d.mts.map +1 -0
- package/dist/verify/index.d.mts +1 -1
- package/dist/verify/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/diff-CSy3g-Bq.mjs.map +0 -1
- package/dist/types-D84LjrVG.d.mts.map +0 -1
|
@@ -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. */
|
|
@@ -629,6 +638,86 @@ function buildFieldFromSchema(fieldName, fieldSchema, contextName, requiredSet)
|
|
|
629
638
|
example: fieldSchema.example
|
|
630
639
|
};
|
|
631
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* If every non-null oneOf/anyOf variant is a string-valued const, return the
|
|
643
|
+
* list of those const values so the union can collapse into a single enum.
|
|
644
|
+
* Returns null when any variant isn't a pure string-const (model ref,
|
|
645
|
+
* number const, nullable, etc.).
|
|
646
|
+
*/
|
|
647
|
+
function collectLiteralStringConsts(variants) {
|
|
648
|
+
if (variants.length === 0) return null;
|
|
649
|
+
const values = [];
|
|
650
|
+
for (const v of variants) {
|
|
651
|
+
if (typeof v.const === "string") {
|
|
652
|
+
values.push(v.const);
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (Array.isArray(v.enum) && v.enum.length === 1 && typeof v.enum[0] === "string") {
|
|
656
|
+
values.push(v.enum[0]);
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
return values;
|
|
662
|
+
}
|
|
663
|
+
/** Build an EnumRef the same way the regular enum path does for naming. */
|
|
664
|
+
function buildSyntheticEnumRef(values, contextName, parentModelName) {
|
|
665
|
+
const baseName = toPascalCase(contextName ?? "UnknownEnum");
|
|
666
|
+
const cleanParent = parentModelName ? stripListItemMarkers(parentModelName) : void 0;
|
|
667
|
+
return {
|
|
668
|
+
kind: "enum",
|
|
669
|
+
name: cleanParent && !baseName.startsWith(cleanParent) ? `${cleanParent}${baseName}` : baseName,
|
|
670
|
+
values
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Detect an implicit discriminator on an oneOf where every variant is an
|
|
675
|
+
* object schema that pins the same property to a const value. Returns
|
|
676
|
+
* { property, mapping } so the IR can represent the union as discriminated
|
|
677
|
+
* without needing an explicit `discriminator:` key in the spec.
|
|
678
|
+
*
|
|
679
|
+
* Both shapes are supported:
|
|
680
|
+
* { properties: { event: { const: "x" } }, required: ["event"] }
|
|
681
|
+
* { properties: { event: { type: "string", enum: ["x"] } } }
|
|
682
|
+
*
|
|
683
|
+
* Returns null when any variant doesn't fit the pattern or when variants
|
|
684
|
+
* disagree on which property carries the discriminator.
|
|
685
|
+
*/
|
|
686
|
+
function detectConstPropertyDiscriminator(variants) {
|
|
687
|
+
if (variants.length < 2) return null;
|
|
688
|
+
let candidateProperty = null;
|
|
689
|
+
const candidates = Object.keys(variants[0]?.properties ?? {});
|
|
690
|
+
for (const propName of candidates) if (variants.every((v) => getConstPropertyValue(v, propName) !== null)) {
|
|
691
|
+
candidateProperty = propName;
|
|
692
|
+
break;
|
|
693
|
+
}
|
|
694
|
+
if (!candidateProperty) return null;
|
|
695
|
+
const mapping = {};
|
|
696
|
+
for (const v of variants) {
|
|
697
|
+
const value = getConstPropertyValue(v, candidateProperty);
|
|
698
|
+
if (value === null) return null;
|
|
699
|
+
const variantName = resolveVariantModelName(v);
|
|
700
|
+
if (!variantName) return null;
|
|
701
|
+
mapping[value] = variantName;
|
|
702
|
+
}
|
|
703
|
+
return {
|
|
704
|
+
property: candidateProperty,
|
|
705
|
+
mapping
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function getConstPropertyValue(schema, property) {
|
|
709
|
+
const prop = schema.properties?.[property];
|
|
710
|
+
if (!prop) return null;
|
|
711
|
+
if (typeof prop.const === "string") return prop.const;
|
|
712
|
+
if (Array.isArray(prop.enum) && prop.enum.length === 1 && typeof prop.enum[0] === "string") return prop.enum[0];
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
function resolveVariantModelName(schema) {
|
|
716
|
+
if (schema.$ref) return resolveSchemaName(schema.$ref.replace(/^#\/components\/schemas\//, ""));
|
|
717
|
+
const title = typeof schema["title"] === "string" ? schema["title"] : null;
|
|
718
|
+
if (title) return resolveSchemaName(title);
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
632
721
|
function extractEnum(name, schema) {
|
|
633
722
|
return {
|
|
634
723
|
name,
|
|
@@ -647,6 +736,19 @@ function extractModel(name, schema, schemas) {
|
|
|
647
736
|
if (!fieldSchema) continue;
|
|
648
737
|
fields.push(buildFieldFromSchema(fieldName, fieldSchema, name, requiredSet));
|
|
649
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
|
+
}
|
|
650
752
|
if (schema.additionalProperties && typeof schema.additionalProperties === "object" && schema.properties) {
|
|
651
753
|
if (Object.keys(schema.additionalProperties).length > 0) {
|
|
652
754
|
const valueType = schemaToTypeRef(schema.additionalProperties, "additionalProperties", name);
|
|
@@ -686,6 +788,18 @@ function extractAllOfModel(name, schema, schemas) {
|
|
|
686
788
|
});
|
|
687
789
|
if (f.required) requiredSet.add(f.name);
|
|
688
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
|
+
}
|
|
689
803
|
} else {
|
|
690
804
|
if (resolved.required) for (const r of resolved.required) requiredSet.add(r);
|
|
691
805
|
if (resolved.properties) {
|
|
@@ -852,6 +966,15 @@ function schemaToTypeRef(schema, contextName, parentModelName) {
|
|
|
852
966
|
kind: "nullable",
|
|
853
967
|
inner: schemaToTypeRef(nonNullVariants[0], contextName, parentModelName)
|
|
854
968
|
};
|
|
969
|
+
const literalStrings = collectLiteralStringConsts(nonNullVariants);
|
|
970
|
+
if (literalStrings !== null && literalStrings.length >= 2) {
|
|
971
|
+
const enumRef = buildSyntheticEnumRef(literalStrings, contextName, parentModelName);
|
|
972
|
+
return nullVariant ? {
|
|
973
|
+
kind: "nullable",
|
|
974
|
+
inner: enumRef
|
|
975
|
+
} : enumRef;
|
|
976
|
+
}
|
|
977
|
+
const syntheticDiscriminator = !schema.discriminator && compositionKind === "oneOf" ? detectConstPropertyDiscriminator(nonNullVariants) : null;
|
|
855
978
|
const variants = rawVariants.filter((v) => v.type !== "null").map((v) => schemaToTypeRef(v, contextName, parentModelName));
|
|
856
979
|
const hasNull = !!nullVariant;
|
|
857
980
|
const union = {
|
|
@@ -861,7 +984,7 @@ function schemaToTypeRef(schema, contextName, parentModelName) {
|
|
|
861
984
|
...schema.discriminator ? { discriminator: {
|
|
862
985
|
property: schema.discriminator.propertyName,
|
|
863
986
|
mapping: Object.fromEntries(Object.entries(schema.discriminator.mapping ?? {}).map(([k, v]) => [k, v.replace(/^#\/components\/schemas\//, "")]))
|
|
864
|
-
} } : {}
|
|
987
|
+
} } : syntheticDiscriminator ? { discriminator: syntheticDiscriminator } : {}
|
|
865
988
|
};
|
|
866
989
|
return hasNull ? {
|
|
867
990
|
kind: "nullable",
|
|
@@ -1533,6 +1656,94 @@ function inferDeeperContext(path) {
|
|
|
1533
1656
|
if (nonParams.length >= 3) return toPascalCase(nonParams.slice(1, -1).join("_"));
|
|
1534
1657
|
return null;
|
|
1535
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
|
+
}
|
|
1536
1747
|
function buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName) {
|
|
1537
1748
|
const allParams = [...pathLevelParams, ...op.parameters ?? []];
|
|
1538
1749
|
const hasIdempotencyHeader = allParams.some((p) => p.in === "header" && p.name.toLowerCase() === "idempotency-key");
|
|
@@ -1558,6 +1769,16 @@ function buildOperation(method, path, op, pathLevelParams, operationIdTransform,
|
|
|
1558
1769
|
};
|
|
1559
1770
|
} else if (paginationFromParams) pagination = paginationFromParams;
|
|
1560
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;
|
|
1561
1782
|
return {
|
|
1562
1783
|
operation: {
|
|
1563
1784
|
name: inferOperationName(method, path, op.operationId, operationIdTransform),
|
|
@@ -1577,7 +1798,8 @@ function buildOperation(method, path, op, pathLevelParams, operationIdTransform,
|
|
|
1577
1798
|
injectIdempotencyKey: hasIdempotencyHeader,
|
|
1578
1799
|
deprecated: op.deprecated || void 0,
|
|
1579
1800
|
async: op["x-oagen-async"],
|
|
1580
|
-
security
|
|
1801
|
+
security,
|
|
1802
|
+
parameterGroups
|
|
1581
1803
|
},
|
|
1582
1804
|
inlineModels
|
|
1583
1805
|
};
|