@workos/oagen 0.16.0 → 0.17.1
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 +183 -18
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.mjs +183 -18
- package/dist/index.mjs.map +1 -1
- package/dist/scripts/smoke/baseline.mjs +183 -18
- package/dist/scripts/smoke/baseline.mjs.map +1 -1
- package/dist/smoke.mjs +183 -18
- package/dist/smoke.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -208,17 +208,64 @@ function extractInlineModelsFromProperties(schema, results, parentName) {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
if (fieldSchema.oneOf) {
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
211
|
+
const inlineObjectVariants = fieldSchema.oneOf.filter((v) => !v.$ref && v.properties && (v.type === "object" || !v.type));
|
|
212
|
+
if (inlineObjectVariants.length > 0) {
|
|
213
213
|
const modelName = qualifyInlineModelName(toPascalCase(fieldName), parentName);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
const existingNames = new Set(results.map((r) => r.name));
|
|
215
|
+
const namingDiscProp = deriveOneOfNamingDiscriminator(inlineObjectVariants);
|
|
216
|
+
const emittedNames = [];
|
|
217
|
+
for (const variant of inlineObjectVariants) {
|
|
218
|
+
const variantName = nameOneOfVariant(variant, modelName, emittedNames, namingDiscProp);
|
|
219
|
+
emittedNames.push(variantName);
|
|
220
|
+
if (!existingNames.has(variantName)) {
|
|
221
|
+
existingNames.add(variantName);
|
|
222
|
+
results.push(buildInlineModel(variantName, variant));
|
|
223
|
+
extractInlineModelsFromProperties(variant, results, variantName);
|
|
224
|
+
}
|
|
217
225
|
}
|
|
218
226
|
}
|
|
219
227
|
}
|
|
220
228
|
}
|
|
221
229
|
}
|
|
230
|
+
/** Find a single string-const-valued property shared by every variant whose
|
|
231
|
+
* values are all distinct — the implicit discriminator. Returns null when no
|
|
232
|
+
* such property exists. Mirrors `deriveConstNamingDiscriminator` in schemas.ts. */
|
|
233
|
+
function deriveOneOfNamingDiscriminator(variants) {
|
|
234
|
+
if (variants.length < 2) return null;
|
|
235
|
+
const candidates = Object.keys(variants[0]?.properties ?? {});
|
|
236
|
+
for (const propName of candidates) {
|
|
237
|
+
const values = variants.map((v) => readConstString(v.properties?.[propName]));
|
|
238
|
+
if (values.some((v) => v === null)) continue;
|
|
239
|
+
if (new Set(values).size !== values.length) continue;
|
|
240
|
+
return propName;
|
|
241
|
+
}
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
function readConstString(prop) {
|
|
245
|
+
if (!prop) return null;
|
|
246
|
+
if (typeof prop.const === "string") return prop.const;
|
|
247
|
+
if (Array.isArray(prop.enum) && prop.enum.length === 1 && typeof prop.enum[0] === "string") return prop.enum[0];
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
/** Produce a per-variant model name. Variant 0 keeps the bare parent name;
|
|
251
|
+
* later variants are prefixed by the const-derived label. Mirrors
|
|
252
|
+
* `nameVariantModel` in schemas.ts. Falls back to a numeric suffix when no
|
|
253
|
+
* discriminator is available, the const value PascalCases to nothing, or
|
|
254
|
+
* the derived candidate collides with the parent or an already-emitted name. */
|
|
255
|
+
function nameOneOfVariant(variant, parentName, alreadyEmitted, discriminatorProperty) {
|
|
256
|
+
if (alreadyEmitted.length === 0) return parentName;
|
|
257
|
+
if (discriminatorProperty) {
|
|
258
|
+
const constValue = readConstString(variant.properties?.[discriminatorProperty]);
|
|
259
|
+
if (constValue) {
|
|
260
|
+
const prefix = toPascalCase(constValue);
|
|
261
|
+
if (prefix) {
|
|
262
|
+
const candidate = parentName.startsWith(prefix) ? parentName : `${prefix}${parentName}`;
|
|
263
|
+
if (!(candidate === parentName || alreadyEmitted.includes(candidate))) return candidate;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return `${parentName}${alreadyEmitted.length + 1}`;
|
|
268
|
+
}
|
|
222
269
|
function buildInlineModel(name, schema) {
|
|
223
270
|
const requiredSet = new Set(schema.required ?? []);
|
|
224
271
|
const fields = [];
|
|
@@ -239,6 +286,14 @@ function buildInlineModel(name, schema) {
|
|
|
239
286
|
* to apply the same name transform to $ref model/enum references.
|
|
240
287
|
*/
|
|
241
288
|
let activeSchemaNameTransform = null;
|
|
289
|
+
/**
|
|
290
|
+
* Module-level set of resolved schema names whose components/schemas entry is
|
|
291
|
+
* an enum (i.e. has `enum:`). Populated by extractSchemas() so schemaToTypeRef()
|
|
292
|
+
* can classify a `$ref` to a top-level enum schema as `kind: 'enum'` instead of
|
|
293
|
+
* `kind: 'model'`. Without this, every `$ref` was naively returned as a model
|
|
294
|
+
* ref, even when the target was a shared enum like `PaginationOrder`.
|
|
295
|
+
*/
|
|
296
|
+
let activeEnumSchemaNames = /* @__PURE__ */ new Set();
|
|
242
297
|
/** Apply cleanSchemaName + the active transform (if any) to a raw schema name. */
|
|
243
298
|
function resolveSchemaName(rawName) {
|
|
244
299
|
let name = cleanSchemaName(toPascalCase(rawName));
|
|
@@ -264,6 +319,8 @@ function extractSchemas(schemas, options) {
|
|
|
264
319
|
for (const [, originals] of transformedToOriginals) if (originals.length > 1) for (const n of originals) unsafeToTransform.add(n);
|
|
265
320
|
activeSchemaNameTransform = (name) => unsafeToTransform.has(name) ? name : transform(name);
|
|
266
321
|
} else activeSchemaNameTransform = null;
|
|
322
|
+
activeEnumSchemaNames = /* @__PURE__ */ new Set();
|
|
323
|
+
for (const [name, schema] of Object.entries(schemas)) if (schema.enum) activeEnumSchemaNames.add(resolveSchemaName(name));
|
|
267
324
|
const modelsByCleanName = /* @__PURE__ */ new Map();
|
|
268
325
|
for (const [name, schema] of Object.entries(schemas)) {
|
|
269
326
|
const pascalName = resolveSchemaName(name);
|
|
@@ -298,6 +355,7 @@ function extractSchemas(schemas, options) {
|
|
|
298
355
|
/** Clear the module-level schemaNameTransform. Called by parseSpec() after all extraction phases. */
|
|
299
356
|
function clearSchemaNameTransform() {
|
|
300
357
|
activeSchemaNameTransform = null;
|
|
358
|
+
activeEnumSchemaNames = /* @__PURE__ */ new Set();
|
|
301
359
|
}
|
|
302
360
|
/**
|
|
303
361
|
* Walk a single TypeRef and extract inline enum refs as top-level enum definitions.
|
|
@@ -585,6 +643,51 @@ function resolveVariantModelName(schema) {
|
|
|
585
643
|
if (title) return resolveSchemaName(title);
|
|
586
644
|
return null;
|
|
587
645
|
}
|
|
646
|
+
/** Look up the discriminator's mapped variant name for an inline object
|
|
647
|
+
* variant by reading its const-valued discriminator property. Returns null
|
|
648
|
+
* when the variant doesn't pin the property to a string const, or when
|
|
649
|
+
* the value isn't in the mapping. */
|
|
650
|
+
function mapVariantToDiscriminatorEntry(variant, discriminator) {
|
|
651
|
+
const value = getConstPropertyValue(variant, discriminator.property);
|
|
652
|
+
if (value === null) return null;
|
|
653
|
+
return discriminator.mapping[value] ?? null;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Derive a discriminator value → variant model name mapping for an inline-
|
|
657
|
+
* variant `oneOf` whose explicit `discriminator:` has no `mapping:` clause.
|
|
658
|
+
*
|
|
659
|
+
* Each variant model name is reproduced via `nameVariantModel` so it matches
|
|
660
|
+
* what `extractNestedSchema` registered when it walked the same `oneOf` to
|
|
661
|
+
* pull variants out as named models. Variant 0 keeps the bare parent name
|
|
662
|
+
* (e.g. `ApiKeyOwner`); subsequent variants get a const-derived prefix
|
|
663
|
+
* (e.g. `UserApiKeyOwner` for the `type: const: user` variant).
|
|
664
|
+
*
|
|
665
|
+
* The "parent name" used here mirrors the one `collectNestedInlineModels`
|
|
666
|
+
* passes to `extractNestedSchema`: it's the qualified inline model name
|
|
667
|
+
* built from `contextName` (the field name) plus `parentModelName` (the
|
|
668
|
+
* containing model). Returns null when any non-object variant is present
|
|
669
|
+
* or when a variant lacks a const value on the discriminator property.
|
|
670
|
+
*/
|
|
671
|
+
function deriveInlineVariantMapping(variants, discriminatorProperty, contextName, parentModelName) {
|
|
672
|
+
if (variants.length === 0 || !contextName) return null;
|
|
673
|
+
const inlineObjectVariants = variants.filter((v) => !v.$ref && v.properties && (v.type === "object" || !v.type));
|
|
674
|
+
if (inlineObjectVariants.length !== variants.length) return null;
|
|
675
|
+
const baseInlineName = qualifyInlineModelName(toPascalCase(contextName), parentModelName);
|
|
676
|
+
const namingDiscriminator = { property: discriminatorProperty };
|
|
677
|
+
const mapping = {};
|
|
678
|
+
const emittedSoFar = [];
|
|
679
|
+
for (const variant of inlineObjectVariants) {
|
|
680
|
+
const constValue = getConstPropertyValue(variant, discriminatorProperty);
|
|
681
|
+
if (constValue === null) return null;
|
|
682
|
+
const variantName = nameVariantModel(variant, baseInlineName, emittedSoFar, namingDiscriminator);
|
|
683
|
+
mapping[constValue] = variantName;
|
|
684
|
+
emittedSoFar.push({
|
|
685
|
+
name: variantName,
|
|
686
|
+
fields: []
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
return mapping;
|
|
690
|
+
}
|
|
588
691
|
function extractEnum(name, schema) {
|
|
589
692
|
return {
|
|
590
693
|
name,
|
|
@@ -783,8 +886,20 @@ function extractInlineModelDeep(name, schema) {
|
|
|
783
886
|
}));
|
|
784
887
|
}
|
|
785
888
|
if (fieldSchema.oneOf) {
|
|
786
|
-
const
|
|
787
|
-
if (
|
|
889
|
+
const inlineObjectVariants = fieldSchema.oneOf.filter((v) => !v.$ref && v.properties && (v.type === "object" || !v.type));
|
|
890
|
+
if (inlineObjectVariants.length > 0) {
|
|
891
|
+
const baseQualified = qualifyNestedInlineName(name, fieldName);
|
|
892
|
+
const namingDisc = deriveConstNamingDiscriminator(inlineObjectVariants);
|
|
893
|
+
const emitted = [];
|
|
894
|
+
for (const variant of inlineObjectVariants) {
|
|
895
|
+
const variantName = nameVariantModel(variant, baseQualified, emitted, namingDisc);
|
|
896
|
+
emitted.push({
|
|
897
|
+
name: variantName,
|
|
898
|
+
fields: []
|
|
899
|
+
});
|
|
900
|
+
nestedModels.push(...extractInlineModelDeep(variantName, variant));
|
|
901
|
+
}
|
|
902
|
+
}
|
|
788
903
|
}
|
|
789
904
|
}
|
|
790
905
|
return [{
|
|
@@ -805,9 +920,14 @@ function schemaToTypeRef(schema, contextName, parentModelName) {
|
|
|
805
920
|
if (schema.$ref) {
|
|
806
921
|
const segments = schema.$ref.split("/");
|
|
807
922
|
const rawName = segments[segments.length - 1];
|
|
923
|
+
const resolved = resolveSchemaName(rawName);
|
|
924
|
+
if (activeEnumSchemaNames.has(resolved)) return {
|
|
925
|
+
kind: "enum",
|
|
926
|
+
name: resolved
|
|
927
|
+
};
|
|
808
928
|
return {
|
|
809
929
|
kind: "model",
|
|
810
|
-
name:
|
|
930
|
+
name: resolved
|
|
811
931
|
};
|
|
812
932
|
}
|
|
813
933
|
if (Array.isArray(schema.type)) {
|
|
@@ -881,17 +1001,49 @@ function schemaToTypeRef(schema, contextName, parentModelName) {
|
|
|
881
1001
|
inner: enumRef
|
|
882
1002
|
} : enumRef;
|
|
883
1003
|
}
|
|
884
|
-
|
|
885
|
-
|
|
1004
|
+
let resolvedDiscriminator;
|
|
1005
|
+
if (schema.discriminator) {
|
|
1006
|
+
const property = schema.discriminator.propertyName;
|
|
1007
|
+
const explicitMapping = schema.discriminator.mapping ?? {};
|
|
1008
|
+
let mapping = Object.fromEntries(Object.entries(explicitMapping).map(([k, v]) => [k, v.replace(/^#\/components\/schemas\//, "")]));
|
|
1009
|
+
if (Object.keys(mapping).length === 0) {
|
|
1010
|
+
const inlineMapping = deriveInlineVariantMapping(nonNullVariants, property, contextName, parentModelName);
|
|
1011
|
+
if (inlineMapping) mapping = inlineMapping;
|
|
1012
|
+
}
|
|
1013
|
+
resolvedDiscriminator = {
|
|
1014
|
+
property,
|
|
1015
|
+
mapping
|
|
1016
|
+
};
|
|
1017
|
+
} else if (compositionKind === "oneOf") {
|
|
1018
|
+
const synthetic = detectConstPropertyDiscriminator(nonNullVariants);
|
|
1019
|
+
if (synthetic) resolvedDiscriminator = synthetic;
|
|
1020
|
+
else {
|
|
1021
|
+
const namingDisc = deriveConstNamingDiscriminator(nonNullVariants);
|
|
1022
|
+
if (namingDisc) {
|
|
1023
|
+
const inlineMapping = deriveInlineVariantMapping(nonNullVariants, namingDisc.property, contextName, parentModelName);
|
|
1024
|
+
if (inlineMapping) resolvedDiscriminator = {
|
|
1025
|
+
property: namingDisc.property,
|
|
1026
|
+
mapping: inlineMapping
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
const variants = [];
|
|
1032
|
+
for (const v of rawVariants) {
|
|
1033
|
+
if (v === nullVariant) continue;
|
|
1034
|
+
const inlineMappingName = resolvedDiscriminator && !v.$ref && v.properties && (v.type === "object" || !v.type) ? mapVariantToDiscriminatorEntry(v, resolvedDiscriminator) : null;
|
|
1035
|
+
if (inlineMappingName) variants.push({
|
|
1036
|
+
kind: "model",
|
|
1037
|
+
name: inlineMappingName
|
|
1038
|
+
});
|
|
1039
|
+
else variants.push(schemaToTypeRef(v, contextName, parentModelName));
|
|
1040
|
+
}
|
|
886
1041
|
const hasNull = !!nullVariant;
|
|
887
1042
|
const union = {
|
|
888
1043
|
kind: "union",
|
|
889
1044
|
variants,
|
|
890
1045
|
compositionKind,
|
|
891
|
-
...
|
|
892
|
-
property: schema.discriminator.propertyName,
|
|
893
|
-
mapping: Object.fromEntries(Object.entries(schema.discriminator.mapping ?? {}).map(([k, v]) => [k, v.replace(/^#\/components\/schemas\//, "")]))
|
|
894
|
-
} } : syntheticDiscriminator ? { discriminator: syntheticDiscriminator } : {}
|
|
1046
|
+
...resolvedDiscriminator ? { discriminator: resolvedDiscriminator } : {}
|
|
895
1047
|
};
|
|
896
1048
|
return hasNull ? {
|
|
897
1049
|
kind: "nullable",
|
|
@@ -1463,10 +1615,23 @@ function inferServiceName(path, tag) {
|
|
|
1463
1615
|
if (first.startsWith("{")) return "Default";
|
|
1464
1616
|
return toPascalCase(first);
|
|
1465
1617
|
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Strip overload indices (e.g. NestJS emits `Foo_bar[0]` when a controller
|
|
1620
|
+
* method handles multiple routes) from an operationId so derived names never
|
|
1621
|
+
* leak the bracketed digit. Without this, `authenticate[0]` would surface as
|
|
1622
|
+
* `authenticate0` in method names and `Authenticate0Request` in body model
|
|
1623
|
+
* names. Brackets at the end (or with trailing whitespace) are common in
|
|
1624
|
+
* NestJS-style backends; this regex is intentionally narrow to avoid eating
|
|
1625
|
+
* legitimate digits like `oauth2`.
|
|
1626
|
+
*/
|
|
1627
|
+
function normalizeOperationIdForNaming(operationId) {
|
|
1628
|
+
return operationId.replace(/\[\d+\]\s*$/, "");
|
|
1629
|
+
}
|
|
1466
1630
|
function inferOperationName(method, path, operationId, operationIdTransform) {
|
|
1467
1631
|
if (operationId) {
|
|
1468
|
-
|
|
1469
|
-
return
|
|
1632
|
+
const normalized = normalizeOperationIdForNaming(operationId);
|
|
1633
|
+
if (operationIdTransform) return operationIdTransform(normalized);
|
|
1634
|
+
return toCamelCase(normalized);
|
|
1470
1635
|
}
|
|
1471
1636
|
const verb = inferVerb(method, path);
|
|
1472
1637
|
const resource = inferResourceFromPath(path);
|
|
@@ -1784,7 +1949,7 @@ function extractRequestBody(body, op, inlineModels) {
|
|
|
1784
1949
|
};
|
|
1785
1950
|
return {};
|
|
1786
1951
|
}
|
|
1787
|
-
const contextName = cleanSchemaName(op?.operationId ? toPascalCase(op.operationId) + "Request" : "RequestBody");
|
|
1952
|
+
const contextName = cleanSchemaName(op?.operationId ? toPascalCase(normalizeOperationIdForNaming(op.operationId)) + "Request" : "RequestBody");
|
|
1788
1953
|
if (schema.properties && (schema.type === "object" || !schema.type)) {
|
|
1789
1954
|
const requiredSet = new Set(schema.required ?? []);
|
|
1790
1955
|
const fields = [];
|
|
@@ -1865,7 +2030,7 @@ function deriveOneOfVariantName(variant, baseName, existingModels) {
|
|
|
1865
2030
|
return name;
|
|
1866
2031
|
}
|
|
1867
2032
|
function deriveResponseName(op, path, method) {
|
|
1868
|
-
if (op?.operationId) return cleanSchemaName(toPascalCase(op.operationId) + "Response");
|
|
2033
|
+
if (op?.operationId) return cleanSchemaName(toPascalCase(normalizeOperationIdForNaming(op.operationId)) + "Response");
|
|
1869
2034
|
return toPascalCase(path.split("/").filter(Boolean)[0] ?? "Unknown") + toPascalCase(method) + "Response";
|
|
1870
2035
|
}
|
|
1871
2036
|
function extractResponses(responses, op, path, method) {
|