@workos/oagen-emitters 0.13.0 → 0.14.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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{plugin-B9F2jmwy.mjs → plugin-DRGwxN88.mjs} +754 -49
- package/dist/plugin-DRGwxN88.mjs.map +1 -0
- package/dist/plugin.mjs +1 -1
- package/package.json +2 -2
- package/src/dotnet/models.ts +31 -6
- package/src/dotnet/type-map.ts +18 -1
- package/src/go/models.ts +12 -3
- package/src/kotlin/models.ts +16 -6
- package/src/node/discriminated-models.ts +735 -0
- package/src/node/index.ts +134 -9
- package/src/node/models.ts +23 -3
- package/src/node/node-overrides.ts +49 -6
- package/src/node/utils.ts +5 -1
- package/src/php/index.ts +25 -2
- package/src/php/models.ts +11 -2
- package/src/python/models.ts +12 -2
- package/src/python/resources.ts +8 -2
- package/src/ruby/index.ts +27 -2
- package/src/ruby/models.ts +13 -3
- package/src/rust/index.ts +26 -2
- package/src/rust/models.ts +5 -1
- package/src/rust/resources.ts +4 -1
- package/src/shared/model-utils.ts +49 -7
- package/test/rust/models.test.ts +3 -3
- package/dist/plugin-B9F2jmwy.mjs.map +0 -1
|
@@ -2286,6 +2286,24 @@ dumper.dump;
|
|
|
2286
2286
|
//#endregion
|
|
2287
2287
|
//#region src/shared/model-utils.ts
|
|
2288
2288
|
/**
|
|
2289
|
+
* Collect model names referenced as the return type of any non-paginated
|
|
2290
|
+
* operation. The list-wrapper skip rule below assumes a wrapper is always
|
|
2291
|
+
* replaced by the SDK's pagination machinery — but a few endpoints
|
|
2292
|
+
* (e.g. `GET /vault/v1/kv/{id}/versions`) have a list-envelope response
|
|
2293
|
+
* shape with no pagination params, so the parser leaves them as a plain
|
|
2294
|
+
* model reference. We must still emit those wrappers as regular models;
|
|
2295
|
+
* otherwise the generated resource code references an undefined name.
|
|
2296
|
+
*/
|
|
2297
|
+
function collectNonPaginatedResponseModelNames(services) {
|
|
2298
|
+
const names = /* @__PURE__ */ new Set();
|
|
2299
|
+
for (const service of services) for (const op of service.operations) {
|
|
2300
|
+
if (op.pagination) continue;
|
|
2301
|
+
walkTypeRef(op.response, { model: (r) => names.add(r.name) });
|
|
2302
|
+
for (const sr of op.successResponses ?? []) walkTypeRef(sr.type, { model: (r) => names.add(r.name) });
|
|
2303
|
+
}
|
|
2304
|
+
return names;
|
|
2305
|
+
}
|
|
2306
|
+
/**
|
|
2289
2307
|
* Detect whether a model is a list wrapper -- the standard paginated
|
|
2290
2308
|
* list envelope with `data` (array), `list_metadata`, and optionally `object: 'list'`.
|
|
2291
2309
|
*
|
|
@@ -2636,7 +2654,7 @@ function detectDiscriminators(models) {
|
|
|
2636
2654
|
* Returns a new array of enriched models (original models are not mutated).
|
|
2637
2655
|
* Synthetic enums are stored internally; retrieve them via `getSyntheticEnums()`.
|
|
2638
2656
|
*/
|
|
2639
|
-
function enrichModelsFromSpec(models) {
|
|
2657
|
+
function enrichModelsFromSpec(models, enums = []) {
|
|
2640
2658
|
if (!loadRawSpec()) {
|
|
2641
2659
|
_lastSyntheticEnums = [];
|
|
2642
2660
|
return models;
|
|
@@ -2646,6 +2664,10 @@ function enrichModelsFromSpec(models) {
|
|
|
2646
2664
|
collector.usedNames.add(m.name);
|
|
2647
2665
|
collector.usedNames.add(toSnakeCase(m.name));
|
|
2648
2666
|
}
|
|
2667
|
+
for (const e of enums) {
|
|
2668
|
+
collector.usedNames.add(e.name);
|
|
2669
|
+
collector.usedNames.add(toSnakeCase(e.name));
|
|
2670
|
+
}
|
|
2649
2671
|
const enriched2 = models.map((model) => {
|
|
2650
2672
|
const rawSchema = lookupRawSchema(model.name);
|
|
2651
2673
|
if (!rawSchema) return model;
|
|
@@ -2692,6 +2714,7 @@ function enrichModelsFromSpec(models) {
|
|
|
2692
2714
|
_lastSyntheticEnums = collector.enums.map((e) => ({
|
|
2693
2715
|
name: e.name,
|
|
2694
2716
|
values: e.values.map((v) => ({
|
|
2717
|
+
name: toUpperSnakeCase(String(v.value)),
|
|
2695
2718
|
value: v.value,
|
|
2696
2719
|
description: v.description
|
|
2697
2720
|
}))
|
|
@@ -2985,7 +3008,7 @@ function mapTypeRef$7(ref, opts) {
|
|
|
2985
3008
|
const genericDefaults = opts?.genericDefaults;
|
|
2986
3009
|
return mapTypeRef(ref, {
|
|
2987
3010
|
primitive: mapPrimitive$6,
|
|
2988
|
-
array: (_r, items) => `${parenthesizeUnion(items)}[]`,
|
|
3011
|
+
array: (_r, items) => `${parenthesizeUnion$1(items)}[]`,
|
|
2989
3012
|
model: (r) => resolveDomainName(r.name) + (genericDefaults?.get(r.name) ?? ""),
|
|
2990
3013
|
enum: (r) => inlineEnumUnions.get(r.name) ?? r.name,
|
|
2991
3014
|
union: (r, variants) => joinUnionVariants$5(r, variants),
|
|
@@ -3002,7 +3025,7 @@ function mapWireTypeRef(ref, opts) {
|
|
|
3002
3025
|
const genericDefaults = opts?.genericDefaults;
|
|
3003
3026
|
return mapTypeRef(ref, {
|
|
3004
3027
|
primitive: mapWirePrimitive,
|
|
3005
|
-
array: (_r, items) => `${parenthesizeUnion(items)}[]`,
|
|
3028
|
+
array: (_r, items) => `${parenthesizeUnion$1(items)}[]`,
|
|
3006
3029
|
model: (r) => wireInterfaceName(resolveDomainName(r.name)) + (genericDefaults?.get(r.name) ?? ""),
|
|
3007
3030
|
enum: (r) => inlineEnumUnions.get(r.name) ?? r.name,
|
|
3008
3031
|
union: (r, variants) => joinUnionVariants$5(r, variants),
|
|
@@ -3039,7 +3062,7 @@ function joinUnionVariants$5(ref, variants) {
|
|
|
3039
3062
|
if (unique.length === 1) return unique[0];
|
|
3040
3063
|
return unique.join(" | ");
|
|
3041
3064
|
}
|
|
3042
|
-
function parenthesizeUnion(type) {
|
|
3065
|
+
function parenthesizeUnion$1(type) {
|
|
3043
3066
|
return type.includes(" | ") || type.includes(" & ") ? `(${type})` : type;
|
|
3044
3067
|
}
|
|
3045
3068
|
//#endregion
|
|
@@ -4627,7 +4650,7 @@ function splitCamelWords(name) {
|
|
|
4627
4650
|
return parts;
|
|
4628
4651
|
}
|
|
4629
4652
|
/** Naive singularize: strip trailing 's' unless it ends in 'ss'. */
|
|
4630
|
-
function singularize$
|
|
4653
|
+
function singularize$4(word) {
|
|
4631
4654
|
return word.endsWith("s") && !word.endsWith("ss") ? word.slice(0, -1) : word;
|
|
4632
4655
|
}
|
|
4633
4656
|
/**
|
|
@@ -4644,11 +4667,11 @@ function deduplicateMethodNames(plans, _ctx) {
|
|
|
4644
4667
|
if (count <= 1) continue;
|
|
4645
4668
|
const dupes = plans.filter((p) => p.method === name);
|
|
4646
4669
|
if (new Set(dupes.map((d) => d.op.path.replace(/\/\{[^}]+\}$/, ""))).size <= 1) continue;
|
|
4647
|
-
const nameWords = new Set(splitCamelWords(name).map(singularize$
|
|
4670
|
+
const nameWords = new Set(splitCamelWords(name).map(singularize$4));
|
|
4648
4671
|
const scored = dupes.map((d) => {
|
|
4649
4672
|
return {
|
|
4650
4673
|
plan: d,
|
|
4651
|
-
score: d.op.path.split("/").filter((s) => s && !s.startsWith("{")).flatMap((s) => s.split("_")).map(singularize$
|
|
4674
|
+
score: d.op.path.split("/").filter((s) => s && !s.startsWith("{")).flatMap((s) => s.split("_")).map(singularize$4).filter((w) => nameWords.has(w)).length
|
|
4652
4675
|
};
|
|
4653
4676
|
});
|
|
4654
4677
|
scored.sort((a, b) => b.score - a.score);
|
|
@@ -5942,6 +5965,7 @@ function isSupportedFieldType(ref, ownerModelName, shared, ctx) {
|
|
|
5942
5965
|
const resolvedName = resolveInterfaceName(ref.name, ctx);
|
|
5943
5966
|
if (ctx.apiSurface?.interfaces?.[resolvedName] || ctx.apiSurface?.typeAliases?.[resolvedName]) return true;
|
|
5944
5967
|
if (isAdoptedModelName(ref.name)) return true;
|
|
5968
|
+
if (shared.modelToService.has(ref.name)) return true;
|
|
5945
5969
|
return liveSurfaceHasManagedFile(`src/${shared.resolveDir(shared.modelToService.get(ref.name))}/interfaces/${fileName$3(ref.name)}.interface.ts`);
|
|
5946
5970
|
}
|
|
5947
5971
|
case "enum": {
|
|
@@ -5988,12 +6012,15 @@ function generateModels$7(models, ctx, shared) {
|
|
|
5988
6012
|
if ((ctx.apiSurface?.interfaces?.[depName])?.sourceFile === parentPath) forceGenerate.add(dep);
|
|
5989
6013
|
}
|
|
5990
6014
|
}
|
|
6015
|
+
const discriminatedSkip = ctx._discriminatedModelNames;
|
|
6016
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
5991
6017
|
for (const originalModel of models) {
|
|
5992
6018
|
const model = projectedByName.get(originalModel.name) ?? originalModel;
|
|
5993
6019
|
if (!reachableModels.has(model.name)) continue;
|
|
5994
6020
|
if (interfaceEligibleModels && !interfaceEligibleModels.has(model.name)) continue;
|
|
5995
6021
|
if (isListMetadataModel(model)) continue;
|
|
5996
|
-
if (isListWrapperModel(model)) continue;
|
|
6022
|
+
if (isListWrapperModel(model) && !nonPaginatedRefs.has(model.name)) continue;
|
|
6023
|
+
if (discriminatedSkip?.has(model.name)) continue;
|
|
5997
6024
|
const service = modelToService.get(model.name);
|
|
5998
6025
|
const isOwnedModel = isNodeOwnedService(ctx, service);
|
|
5999
6026
|
if (!isOwnedModel && !modelHasNewFields(model, ctx) && !forceGenerate.has(model.name)) continue;
|
|
@@ -6325,13 +6352,16 @@ function generateSerializers(models, ctx, shared) {
|
|
|
6325
6352
|
if ((ctx.apiSurface?.interfaces?.[depName])?.sourceFile === parentPath) forceGenerateSerializer.add(dep);
|
|
6326
6353
|
}
|
|
6327
6354
|
}
|
|
6355
|
+
const discriminatedSerializerSkip = ctx._discriminatedModelNames;
|
|
6356
|
+
const serializerNonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
6328
6357
|
const eligibleModels = [];
|
|
6329
6358
|
for (const originalModel of models) {
|
|
6330
6359
|
const model = projectedByName.get(originalModel.name) ?? originalModel;
|
|
6331
6360
|
if (!serializerReachable.has(model.name)) continue;
|
|
6332
6361
|
if (serializerEligibleModels && !serializerEligibleModels.has(model.name)) continue;
|
|
6333
6362
|
if (isListMetadataModel(model)) continue;
|
|
6334
|
-
if (isListWrapperModel(model)) continue;
|
|
6363
|
+
if (isListWrapperModel(model) && !serializerNonPaginatedRefs.has(model.name)) continue;
|
|
6364
|
+
if (discriminatedSerializerSkip?.has(model.name)) continue;
|
|
6335
6365
|
if (!isNodeOwnedService(ctx, modelToService.get(model.name)) && !modelHasNewFields(model, ctx) && !forceGenerateSerializer.has(model.name)) continue;
|
|
6336
6366
|
eligibleModels.push(model);
|
|
6337
6367
|
}
|
|
@@ -7643,6 +7673,445 @@ function generateSerializerTests(spec, ctx) {
|
|
|
7643
7673
|
return files;
|
|
7644
7674
|
}
|
|
7645
7675
|
//#endregion
|
|
7676
|
+
//#region src/node/discriminated-models.ts
|
|
7677
|
+
function detectDiscriminatedShape(modelName, rawSchemas) {
|
|
7678
|
+
const schema = rawSchemas[modelName];
|
|
7679
|
+
if (!schema?.allOf) return null;
|
|
7680
|
+
let baseObject = null;
|
|
7681
|
+
let oneOfVariants = null;
|
|
7682
|
+
for (const member of schema.allOf) {
|
|
7683
|
+
const resolved = resolveRef(member, rawSchemas);
|
|
7684
|
+
if (resolved.oneOf) {
|
|
7685
|
+
if (oneOfVariants) return null;
|
|
7686
|
+
oneOfVariants = resolved.oneOf;
|
|
7687
|
+
} else if (resolved.properties) baseObject = mergeBase(baseObject, resolved);
|
|
7688
|
+
else if (resolved.allOf) {
|
|
7689
|
+
const nestedBase = flattenObjectAllOf(resolved, rawSchemas);
|
|
7690
|
+
baseObject = mergeBase(baseObject, nestedBase);
|
|
7691
|
+
}
|
|
7692
|
+
}
|
|
7693
|
+
if (!oneOfVariants || oneOfVariants.length < 2) return null;
|
|
7694
|
+
const flattenedVariants = oneOfVariants.map((v) => flattenVariant(v, rawSchemas));
|
|
7695
|
+
const discProp = findSharedDiscriminator(flattenedVariants);
|
|
7696
|
+
if (!discProp) return null;
|
|
7697
|
+
const variants = flattenedVariants.map((fv) => {
|
|
7698
|
+
const discValue = readConstString(fv.alwaysProperties.get(discProp));
|
|
7699
|
+
if (!discValue) return null;
|
|
7700
|
+
return {
|
|
7701
|
+
nameSuffix: variantNameSuffix(discValue),
|
|
7702
|
+
discriminatorValue: discValue,
|
|
7703
|
+
fields: variantFields(fv, discProp, modelName, rawSchemas)
|
|
7704
|
+
};
|
|
7705
|
+
}).filter((v) => v !== null);
|
|
7706
|
+
if (variants.length !== flattenedVariants.length) return null;
|
|
7707
|
+
return {
|
|
7708
|
+
modelName,
|
|
7709
|
+
baseFields: baseObject ? collectObjectFields(baseObject, modelName, rawSchemas) : [],
|
|
7710
|
+
discriminatorProperty: discProp,
|
|
7711
|
+
discriminatorPropertyDomain: toCamelCase(discProp),
|
|
7712
|
+
variants
|
|
7713
|
+
};
|
|
7714
|
+
}
|
|
7715
|
+
function mergeBase(prev, next) {
|
|
7716
|
+
if (!prev) return next;
|
|
7717
|
+
return {
|
|
7718
|
+
type: "object",
|
|
7719
|
+
properties: {
|
|
7720
|
+
...prev.properties ?? {},
|
|
7721
|
+
...next.properties ?? {}
|
|
7722
|
+
},
|
|
7723
|
+
required: [...new Set([...prev.required ?? [], ...next.required ?? []])]
|
|
7724
|
+
};
|
|
7725
|
+
}
|
|
7726
|
+
function flattenObjectAllOf(schema, rawSchemas) {
|
|
7727
|
+
let merged = {
|
|
7728
|
+
type: "object",
|
|
7729
|
+
properties: {},
|
|
7730
|
+
required: []
|
|
7731
|
+
};
|
|
7732
|
+
for (const sub of schema.allOf ?? []) {
|
|
7733
|
+
const resolved = resolveRef(sub, rawSchemas);
|
|
7734
|
+
if (resolved.properties) merged = mergeBase(merged, resolved);
|
|
7735
|
+
else if (resolved.allOf) merged = mergeBase(merged, flattenObjectAllOf(resolved, rawSchemas));
|
|
7736
|
+
}
|
|
7737
|
+
return merged;
|
|
7738
|
+
}
|
|
7739
|
+
function flattenVariant(variant, rawSchemas) {
|
|
7740
|
+
const resolved = resolveRef(variant, rawSchemas);
|
|
7741
|
+
if (resolved.properties && !resolved.allOf && !resolved.oneOf) {
|
|
7742
|
+
const props = /* @__PURE__ */ new Map();
|
|
7743
|
+
for (const [k, v] of Object.entries(resolved.properties)) props.set(k, v);
|
|
7744
|
+
return {
|
|
7745
|
+
alwaysProperties: props,
|
|
7746
|
+
optionalProperties: /* @__PURE__ */ new Map(),
|
|
7747
|
+
required: new Set(resolved.required ?? [])
|
|
7748
|
+
};
|
|
7749
|
+
}
|
|
7750
|
+
if (resolved.allOf) {
|
|
7751
|
+
let agg = {
|
|
7752
|
+
alwaysProperties: /* @__PURE__ */ new Map(),
|
|
7753
|
+
optionalProperties: /* @__PURE__ */ new Map(),
|
|
7754
|
+
required: /* @__PURE__ */ new Set()
|
|
7755
|
+
};
|
|
7756
|
+
let initialized = false;
|
|
7757
|
+
for (const member of resolved.allOf) {
|
|
7758
|
+
const memberView = flattenVariant(member, rawSchemas);
|
|
7759
|
+
if (!initialized) {
|
|
7760
|
+
agg = {
|
|
7761
|
+
alwaysProperties: new Map(memberView.alwaysProperties),
|
|
7762
|
+
optionalProperties: new Map(memberView.optionalProperties),
|
|
7763
|
+
required: new Set(memberView.required)
|
|
7764
|
+
};
|
|
7765
|
+
initialized = true;
|
|
7766
|
+
continue;
|
|
7767
|
+
}
|
|
7768
|
+
for (const [k, v] of memberView.alwaysProperties) if (!agg.alwaysProperties.has(k) && !agg.optionalProperties.has(k)) agg.alwaysProperties.set(k, v);
|
|
7769
|
+
for (const [k, v] of memberView.optionalProperties) if (!agg.alwaysProperties.has(k) && !agg.optionalProperties.has(k)) agg.optionalProperties.set(k, v);
|
|
7770
|
+
for (const r of memberView.required) agg.required.add(r);
|
|
7771
|
+
}
|
|
7772
|
+
return agg;
|
|
7773
|
+
}
|
|
7774
|
+
if (resolved.oneOf) {
|
|
7775
|
+
const memberViews = resolved.oneOf.map((m) => flattenVariant(m, rawSchemas));
|
|
7776
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
7777
|
+
for (const view of memberViews) {
|
|
7778
|
+
for (const k of view.alwaysProperties.keys()) allKeys.add(k);
|
|
7779
|
+
for (const k of view.optionalProperties.keys()) allKeys.add(k);
|
|
7780
|
+
}
|
|
7781
|
+
const always = /* @__PURE__ */ new Map();
|
|
7782
|
+
const optional = /* @__PURE__ */ new Map();
|
|
7783
|
+
const requiredEverywhere = /* @__PURE__ */ new Set();
|
|
7784
|
+
for (const key of allKeys) {
|
|
7785
|
+
const inAll = memberViews.every((v) => v.alwaysProperties.has(key) || v.optionalProperties.has(key));
|
|
7786
|
+
const merged = mergeFieldSchemas(memberViews.map((v) => v.alwaysProperties.get(key) ?? v.optionalProperties.get(key)).filter((s) => Boolean(s)));
|
|
7787
|
+
if (inAll) always.set(key, merged);
|
|
7788
|
+
else optional.set(key, merged);
|
|
7789
|
+
if (inAll && memberViews.every((v) => v.required.has(key))) requiredEverywhere.add(key);
|
|
7790
|
+
}
|
|
7791
|
+
return {
|
|
7792
|
+
alwaysProperties: always,
|
|
7793
|
+
optionalProperties: optional,
|
|
7794
|
+
required: requiredEverywhere
|
|
7795
|
+
};
|
|
7796
|
+
}
|
|
7797
|
+
return {
|
|
7798
|
+
alwaysProperties: /* @__PURE__ */ new Map(),
|
|
7799
|
+
optionalProperties: /* @__PURE__ */ new Map(),
|
|
7800
|
+
required: /* @__PURE__ */ new Set()
|
|
7801
|
+
};
|
|
7802
|
+
}
|
|
7803
|
+
function mergeFieldSchemas(schemas) {
|
|
7804
|
+
if (schemas.length === 0) return {};
|
|
7805
|
+
if (schemas.length === 1) return schemas[0];
|
|
7806
|
+
if (schemas.every((s) => s.type === "boolean" && typeof s.const === "boolean")) return {
|
|
7807
|
+
type: "boolean",
|
|
7808
|
+
description: schemas[0].description
|
|
7809
|
+
};
|
|
7810
|
+
if (schemas.every((s) => s.type === "string" && typeof s.const === "string")) {
|
|
7811
|
+
const values = schemas.map((s) => s.const);
|
|
7812
|
+
if (new Set(values).size === 1) return schemas[0];
|
|
7813
|
+
return {
|
|
7814
|
+
type: "string",
|
|
7815
|
+
description: schemas[0].description
|
|
7816
|
+
};
|
|
7817
|
+
}
|
|
7818
|
+
return schemas[0];
|
|
7819
|
+
}
|
|
7820
|
+
function findSharedDiscriminator(variants) {
|
|
7821
|
+
if (variants.length < 2) return null;
|
|
7822
|
+
const firstAlways = variants[0].alwaysProperties;
|
|
7823
|
+
for (const propName of firstAlways.keys()) {
|
|
7824
|
+
let allConst = true;
|
|
7825
|
+
const values = [];
|
|
7826
|
+
for (const v of variants) {
|
|
7827
|
+
const val = readConstString(v.alwaysProperties.get(propName));
|
|
7828
|
+
if (val === null) {
|
|
7829
|
+
allConst = false;
|
|
7830
|
+
break;
|
|
7831
|
+
}
|
|
7832
|
+
values.push(val);
|
|
7833
|
+
}
|
|
7834
|
+
if (allConst && new Set(values).size === values.length) return propName;
|
|
7835
|
+
}
|
|
7836
|
+
return null;
|
|
7837
|
+
}
|
|
7838
|
+
function readConstString(schema) {
|
|
7839
|
+
if (!schema) return null;
|
|
7840
|
+
if (typeof schema.const === "string") return schema.const;
|
|
7841
|
+
if (Array.isArray(schema.enum) && schema.enum.length === 1 && typeof schema.enum[0] === "string") return schema.enum[0];
|
|
7842
|
+
return null;
|
|
7843
|
+
}
|
|
7844
|
+
function variantNameSuffix(constValue) {
|
|
7845
|
+
return toPascalCase(constValue);
|
|
7846
|
+
}
|
|
7847
|
+
function collectObjectFields(schema, parentName, rawSchemas) {
|
|
7848
|
+
const props = schema.properties ?? {};
|
|
7849
|
+
const required = new Set(schema.required ?? []);
|
|
7850
|
+
const fields = [];
|
|
7851
|
+
for (const [name, propSchema] of Object.entries(props)) fields.push(buildField(name, propSchema, required.has(name), parentName, rawSchemas));
|
|
7852
|
+
return fields;
|
|
7853
|
+
}
|
|
7854
|
+
function variantFields(fv, discriminatorProperty, parentName, rawSchemas) {
|
|
7855
|
+
const fields = [];
|
|
7856
|
+
for (const [name, propSchema] of fv.alwaysProperties) {
|
|
7857
|
+
if (name === discriminatorProperty) continue;
|
|
7858
|
+
fields.push(buildField(name, propSchema, fv.required.has(name), parentName, rawSchemas));
|
|
7859
|
+
}
|
|
7860
|
+
for (const [name, propSchema] of fv.optionalProperties) {
|
|
7861
|
+
if (name === discriminatorProperty) continue;
|
|
7862
|
+
fields.push(buildField(name, propSchema, false, parentName, rawSchemas));
|
|
7863
|
+
}
|
|
7864
|
+
return fields;
|
|
7865
|
+
}
|
|
7866
|
+
function buildField(rawName, schema, required, parentName, rawSchemas) {
|
|
7867
|
+
const modelDeps = /* @__PURE__ */ new Set();
|
|
7868
|
+
const domainType = rawSchemaToTS(schema, parentName, rawName, false, modelDeps, rawSchemas);
|
|
7869
|
+
const wireType = rawSchemaToTS(schema, parentName, rawName, true, modelDeps, rawSchemas);
|
|
7870
|
+
return {
|
|
7871
|
+
name: rawName,
|
|
7872
|
+
description: schema.description,
|
|
7873
|
+
required,
|
|
7874
|
+
domainType,
|
|
7875
|
+
wireType,
|
|
7876
|
+
modelDeps,
|
|
7877
|
+
hasDateTime: schemaHasDateTime(schema)
|
|
7878
|
+
};
|
|
7879
|
+
}
|
|
7880
|
+
function schemaHasDateTime(schema) {
|
|
7881
|
+
if (schema.format === "date-time" && typeOf(schema) === "string") return true;
|
|
7882
|
+
if (schema.items && schemaHasDateTime(schema.items)) return true;
|
|
7883
|
+
return false;
|
|
7884
|
+
}
|
|
7885
|
+
function typeOf(schema) {
|
|
7886
|
+
if (Array.isArray(schema.type)) return schema.type.find((t) => t !== "null");
|
|
7887
|
+
return schema.type;
|
|
7888
|
+
}
|
|
7889
|
+
function isNullable(schema) {
|
|
7890
|
+
return Array.isArray(schema.type) && schema.type.includes("null");
|
|
7891
|
+
}
|
|
7892
|
+
function rawSchemaToTS(schema, parentName, fieldName, isWire, modelDeps, rawSchemas) {
|
|
7893
|
+
if (schema.$ref) {
|
|
7894
|
+
const refName = schema.$ref.split("/").pop();
|
|
7895
|
+
modelDeps.add(refName);
|
|
7896
|
+
const domain = toPascalCase(refName);
|
|
7897
|
+
return isWire ? wireInterfaceName(domain) : domain;
|
|
7898
|
+
}
|
|
7899
|
+
if (typeof schema.const === "string") return `'${schema.const}'`;
|
|
7900
|
+
if (typeof schema.const === "boolean") return String(schema.const);
|
|
7901
|
+
const baseType = typeOf(schema);
|
|
7902
|
+
const nullable = isNullable(schema);
|
|
7903
|
+
let core;
|
|
7904
|
+
if (baseType === "string") core = !isWire && schema.format === "date-time" ? "Date" : "string";
|
|
7905
|
+
else if (baseType === "integer" || baseType === "number") core = "number";
|
|
7906
|
+
else if (baseType === "boolean") core = "boolean";
|
|
7907
|
+
else if (baseType === "array" && schema.items) core = `${parenthesizeUnion(rawSchemaToTS(schema.items, parentName, singularize$3(fieldName), isWire, modelDeps, rawSchemas))}[]`;
|
|
7908
|
+
else if (baseType === "object" && schema.properties) {
|
|
7909
|
+
const synthName = `${parentName}_${singularize$3(fieldName)}`;
|
|
7910
|
+
modelDeps.add(synthName);
|
|
7911
|
+
const domain = toPascalCase(synthName);
|
|
7912
|
+
return isWire ? wireInterfaceName(domain) : domain;
|
|
7913
|
+
} else core = "unknown";
|
|
7914
|
+
return nullable ? `${core} | null` : core;
|
|
7915
|
+
}
|
|
7916
|
+
function parenthesizeUnion(t) {
|
|
7917
|
+
return /\s\|\s/.test(t) ? `(${t})` : t;
|
|
7918
|
+
}
|
|
7919
|
+
function singularize$3(name) {
|
|
7920
|
+
if (name.endsWith("ies") && name.length > 3) return `${name.slice(0, -3)}y`;
|
|
7921
|
+
if (name.endsWith("s") && !name.endsWith("ss")) return name.slice(0, -1);
|
|
7922
|
+
return name;
|
|
7923
|
+
}
|
|
7924
|
+
function resolveRef(schema, rawSchemas) {
|
|
7925
|
+
if (!schema.$ref) return schema;
|
|
7926
|
+
const segments = schema.$ref.split("/");
|
|
7927
|
+
return rawSchemas[segments[segments.length - 1]] ?? schema;
|
|
7928
|
+
}
|
|
7929
|
+
function planDiscriminatedModels(models, ctx) {
|
|
7930
|
+
const plans = /* @__PURE__ */ new Map();
|
|
7931
|
+
const spec = loadRawSpec();
|
|
7932
|
+
if (!spec?.components?.schemas) return plans;
|
|
7933
|
+
const rawSchemas = spec.components.schemas;
|
|
7934
|
+
const { modelToService, resolveDir } = createServiceDirResolver(models, ctx.spec.services, ctx);
|
|
7935
|
+
for (const model of models) {
|
|
7936
|
+
const shape = detectDiscriminatedShape(model.name, rawSchemas);
|
|
7937
|
+
if (!shape) continue;
|
|
7938
|
+
const modelDir = resolveDir(modelToService.get(model.name));
|
|
7939
|
+
plans.set(model.name, {
|
|
7940
|
+
shape,
|
|
7941
|
+
modelDir
|
|
7942
|
+
});
|
|
7943
|
+
}
|
|
7944
|
+
return plans;
|
|
7945
|
+
}
|
|
7946
|
+
function generateDiscriminatedFiles(plans, ctx) {
|
|
7947
|
+
const files = [];
|
|
7948
|
+
for (const plan of plans.values()) {
|
|
7949
|
+
files.push(buildInterfaceFile(plan, ctx));
|
|
7950
|
+
files.push(buildSerializerFile(plan, ctx));
|
|
7951
|
+
}
|
|
7952
|
+
return files;
|
|
7953
|
+
}
|
|
7954
|
+
function buildInterfaceFile(plan, _ctx) {
|
|
7955
|
+
const { shape, modelDir } = plan;
|
|
7956
|
+
const domain = toPascalCase(shape.modelName);
|
|
7957
|
+
const wire = wireInterfaceName(domain);
|
|
7958
|
+
const lines = [];
|
|
7959
|
+
const imports = collectImports$2(plan);
|
|
7960
|
+
if (imports.length > 0) {
|
|
7961
|
+
for (const imp of imports) lines.push(`import type { ${imp.symbols.join(", ")} } from '${imp.path}';`);
|
|
7962
|
+
lines.push("");
|
|
7963
|
+
}
|
|
7964
|
+
for (const variant of shape.variants) {
|
|
7965
|
+
const variantDomain = `${domain}${variant.nameSuffix}`;
|
|
7966
|
+
const variantWire = `${variantDomain}Response`;
|
|
7967
|
+
lines.push(...buildInterfaceBody(variantDomain, shape, variant, false));
|
|
7968
|
+
lines.push("");
|
|
7969
|
+
lines.push(...buildInterfaceBody(variantWire, shape, variant, true));
|
|
7970
|
+
lines.push("");
|
|
7971
|
+
}
|
|
7972
|
+
const variantNames = shape.variants.map((v) => `${domain}${v.nameSuffix}`);
|
|
7973
|
+
lines.push(`export type ${domain} = ${variantNames.join(" | ")};`);
|
|
7974
|
+
lines.push("");
|
|
7975
|
+
const wireVariantNames = shape.variants.map((v) => `${domain}${v.nameSuffix}Response`);
|
|
7976
|
+
lines.push(`export type ${wire} = ${wireVariantNames.join(" | ")};`);
|
|
7977
|
+
return {
|
|
7978
|
+
path: `src/${modelDir}/interfaces/${fileName$3(shape.modelName)}.interface.ts`,
|
|
7979
|
+
content: lines.join("\n") + "\n",
|
|
7980
|
+
overwriteExisting: true
|
|
7981
|
+
};
|
|
7982
|
+
}
|
|
7983
|
+
function buildInterfaceBody(name, shape, variant, isWire) {
|
|
7984
|
+
const lines = [];
|
|
7985
|
+
lines.push(`export interface ${name} {`);
|
|
7986
|
+
for (const field of shape.baseFields) pushFieldLine(lines, field, isWire);
|
|
7987
|
+
const discKey = isWire ? shape.discriminatorProperty : shape.discriminatorPropertyDomain;
|
|
7988
|
+
lines.push(` ${discKey}: '${variant.discriminatorValue}';`);
|
|
7989
|
+
for (const field of variant.fields) pushFieldLine(lines, field, isWire);
|
|
7990
|
+
lines.push("}");
|
|
7991
|
+
return lines;
|
|
7992
|
+
}
|
|
7993
|
+
function pushFieldLine(lines, field, isWire) {
|
|
7994
|
+
const key = isWire ? field.name : toCamelCase(field.name);
|
|
7995
|
+
const opt = field.required ? "" : "?";
|
|
7996
|
+
const type = isWire ? field.wireType : field.domainType;
|
|
7997
|
+
if (field.description) lines.push(` /** ${field.description} */`);
|
|
7998
|
+
lines.push(` ${key}${opt}: ${type};`);
|
|
7999
|
+
}
|
|
8000
|
+
function collectImports$2(plan) {
|
|
8001
|
+
const deps = /* @__PURE__ */ new Set();
|
|
8002
|
+
for (const field of plan.shape.baseFields) for (const d of field.modelDeps) deps.add(d);
|
|
8003
|
+
for (const variant of plan.shape.variants) for (const field of variant.fields) for (const d of field.modelDeps) deps.add(d);
|
|
8004
|
+
const symbols = [];
|
|
8005
|
+
for (const dep of [...deps].sort()) {
|
|
8006
|
+
const domain = toPascalCase(dep);
|
|
8007
|
+
symbols.push(domain);
|
|
8008
|
+
const wire = wireInterfaceName(domain);
|
|
8009
|
+
if (wire !== domain) symbols.push(wire);
|
|
8010
|
+
}
|
|
8011
|
+
if (symbols.length === 0) return [];
|
|
8012
|
+
return symbols.map((sym) => {
|
|
8013
|
+
return {
|
|
8014
|
+
path: `./${fileName$3(toSnakeFromPascal(sym.replace(/Response$/, "")))}.interface`,
|
|
8015
|
+
symbols: [sym]
|
|
8016
|
+
};
|
|
8017
|
+
}).reduce((acc, cur) => {
|
|
8018
|
+
const existing = acc.find((a) => a.path === cur.path);
|
|
8019
|
+
if (existing) existing.symbols.push(...cur.symbols);
|
|
8020
|
+
else acc.push(cur);
|
|
8021
|
+
return acc;
|
|
8022
|
+
}, []);
|
|
8023
|
+
}
|
|
8024
|
+
function toSnakeFromPascal(s) {
|
|
8025
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toLowerCase();
|
|
8026
|
+
}
|
|
8027
|
+
function buildSerializerFile(plan, _ctx) {
|
|
8028
|
+
const { shape, modelDir } = plan;
|
|
8029
|
+
const domain = toPascalCase(shape.modelName);
|
|
8030
|
+
const wire = wireInterfaceName(domain);
|
|
8031
|
+
const lines = [];
|
|
8032
|
+
const interfaceImportPath = `../interfaces/${fileName$3(shape.modelName)}.interface`;
|
|
8033
|
+
lines.push(`import type { ${domain}, ${wire} } from '${interfaceImportPath}';`);
|
|
8034
|
+
const allDeps = /* @__PURE__ */ new Set();
|
|
8035
|
+
for (const field of shape.baseFields) for (const d of field.modelDeps) allDeps.add(d);
|
|
8036
|
+
for (const variant of shape.variants) for (const field of variant.fields) for (const d of field.modelDeps) allDeps.add(d);
|
|
8037
|
+
for (const dep of [...allDeps].sort()) {
|
|
8038
|
+
const depDomain = toPascalCase(dep);
|
|
8039
|
+
const depFile = fileName$3(toSnakeFromPascal(depDomain));
|
|
8040
|
+
lines.push(`import { deserialize${depDomain}, serialize${depDomain} } from './${depFile}.serializer';`);
|
|
8041
|
+
}
|
|
8042
|
+
lines.push("");
|
|
8043
|
+
lines.push(`export const deserialize${domain} = (response: ${wire}): ${domain} => {`);
|
|
8044
|
+
lines.push(` switch (response.${shape.discriminatorProperty}) {`);
|
|
8045
|
+
for (const variant of shape.variants) {
|
|
8046
|
+
lines.push(` case '${variant.discriminatorValue}':`);
|
|
8047
|
+
lines.push(` return {`);
|
|
8048
|
+
for (const field of shape.baseFields) lines.push(` ${assignmentLine(field, false, allDeps)},`);
|
|
8049
|
+
lines.push(` ${shape.discriminatorPropertyDomain}: '${variant.discriminatorValue}',`);
|
|
8050
|
+
for (const field of variant.fields) lines.push(` ${assignmentLine(field, false, allDeps)},`);
|
|
8051
|
+
lines.push(` };`);
|
|
8052
|
+
}
|
|
8053
|
+
lines.push(` default:`);
|
|
8054
|
+
lines.push(` throw new Error(\`Unknown ${shape.discriminatorProperty}: \${(response as { ${shape.discriminatorProperty}: string }).${shape.discriminatorProperty}}\`);`);
|
|
8055
|
+
lines.push(` }`);
|
|
8056
|
+
lines.push(`};`);
|
|
8057
|
+
lines.push("");
|
|
8058
|
+
lines.push(`export const serialize${domain} = (model: ${domain}): ${wire} => {`);
|
|
8059
|
+
lines.push(` switch (model.${shape.discriminatorPropertyDomain}) {`);
|
|
8060
|
+
for (const variant of shape.variants) {
|
|
8061
|
+
lines.push(` case '${variant.discriminatorValue}':`);
|
|
8062
|
+
lines.push(` return {`);
|
|
8063
|
+
for (const field of shape.baseFields) lines.push(` ${assignmentLine(field, true, allDeps)},`);
|
|
8064
|
+
lines.push(` ${shape.discriminatorProperty}: '${variant.discriminatorValue}',`);
|
|
8065
|
+
for (const field of variant.fields) lines.push(` ${assignmentLine(field, true, allDeps)},`);
|
|
8066
|
+
lines.push(` };`);
|
|
8067
|
+
}
|
|
8068
|
+
lines.push(` }`);
|
|
8069
|
+
lines.push(`};`);
|
|
8070
|
+
return {
|
|
8071
|
+
path: `src/${modelDir}/serializers/${fileName$3(shape.modelName)}.serializer.ts`,
|
|
8072
|
+
content: lines.join("\n") + "\n",
|
|
8073
|
+
overwriteExisting: true
|
|
8074
|
+
};
|
|
8075
|
+
}
|
|
8076
|
+
function assignmentLine(field, serialize, _allDeps) {
|
|
8077
|
+
const camel = toCamelCase(field.name);
|
|
8078
|
+
const snake = field.name;
|
|
8079
|
+
const lhs = serialize ? snake : camel;
|
|
8080
|
+
const rhsKey = serialize ? camel : snake;
|
|
8081
|
+
const source = serialize ? `model.${rhsKey}` : `response.${rhsKey}`;
|
|
8082
|
+
if (field.hasDateTime) {
|
|
8083
|
+
if (serialize) {
|
|
8084
|
+
if (field.required) return `${lhs}: ${source}.toISOString()`;
|
|
8085
|
+
return `${lhs}: ${source} != null ? ${source}.toISOString() : undefined`;
|
|
8086
|
+
}
|
|
8087
|
+
if (field.required) return `${lhs}: new Date(${source})`;
|
|
8088
|
+
return `${lhs}: ${source} != null ? new Date(${source}) : undefined`;
|
|
8089
|
+
}
|
|
8090
|
+
const arrayDep = arrayItemModelDep(field);
|
|
8091
|
+
if (arrayDep) {
|
|
8092
|
+
const fn = serialize ? `serialize${arrayDep}` : `deserialize${arrayDep}`;
|
|
8093
|
+
if (field.required) return `${lhs}: ${source}.map(${fn})`;
|
|
8094
|
+
return `${lhs}: ${source} != null ? ${source}.map(${fn}) : undefined`;
|
|
8095
|
+
}
|
|
8096
|
+
const scalarDep = scalarModelDepName(field);
|
|
8097
|
+
if (scalarDep) {
|
|
8098
|
+
const fn = serialize ? `serialize${scalarDep}` : `deserialize${scalarDep}`;
|
|
8099
|
+
if (field.required) return `${lhs}: ${fn}(${source})`;
|
|
8100
|
+
return `${lhs}: ${source} != null ? ${fn}(${source}) : undefined`;
|
|
8101
|
+
}
|
|
8102
|
+
return `${lhs}: ${source}`;
|
|
8103
|
+
}
|
|
8104
|
+
function arrayItemModelDep(field) {
|
|
8105
|
+
const m = field.domainType.match(/^([A-Z]\w*)\[\]$/);
|
|
8106
|
+
if (m && field.modelDeps.size > 0) return m[1];
|
|
8107
|
+
return null;
|
|
8108
|
+
}
|
|
8109
|
+
function scalarModelDepName(field) {
|
|
8110
|
+
const stripped = field.domainType.replace(/\s*\|\s*null$/, "");
|
|
8111
|
+
if (/^[A-Z]\w*$/.test(stripped) && field.modelDeps.size === 1) return toPascalCase([...field.modelDeps][0]);
|
|
8112
|
+
return null;
|
|
8113
|
+
}
|
|
8114
|
+
//#endregion
|
|
7646
8115
|
//#region src/node/node-overrides.ts
|
|
7647
8116
|
const OPERATION_OVERRIDES = {
|
|
7648
8117
|
"POST /organizations/{organizationId}/groups": { methodName: "create_group" },
|
|
@@ -7662,31 +8131,71 @@ const contextCache = /* @__PURE__ */ new WeakMap();
|
|
|
7662
8131
|
function operationKey(resolved) {
|
|
7663
8132
|
return `${resolved.operation.httpMethod.toUpperCase()} ${resolved.operation.path}`;
|
|
7664
8133
|
}
|
|
8134
|
+
/**
|
|
8135
|
+
* Apply oneOf / allOf+oneOf enrichment (flattening variant fields onto the
|
|
8136
|
+
* parent model, plus synthetic models/enums for inline shapes) so the rest
|
|
8137
|
+
* of the Node emitter sees a richer `spec.models`.
|
|
8138
|
+
*
|
|
8139
|
+
* Without this, `ConnectApplication` (and any other `allOf [base, oneOf [...]]`
|
|
8140
|
+
* schema whose first variant is itself wrapped in `allOf`) loses every
|
|
8141
|
+
* non-M2M field — the IR parser's discriminator detection silently skips
|
|
8142
|
+
* variants whose properties live behind another `allOf`. Mirrors what the
|
|
8143
|
+
* Go / Kotlin / .NET emitters already do.
|
|
8144
|
+
*
|
|
8145
|
+
* Discriminated bases produced by `enrichModelsFromSpec` get their original
|
|
8146
|
+
* fields restored — Node emits flat interfaces today, not TS sum types, so
|
|
8147
|
+
* an empty base would otherwise drop the common fields.
|
|
8148
|
+
*/
|
|
8149
|
+
function enrichSpecModels(models) {
|
|
8150
|
+
const enriched = enrichModelsFromSpec(models);
|
|
8151
|
+
const originalByName = new Map(models.map((m) => [m.name, m]));
|
|
8152
|
+
return enriched.map((m) => {
|
|
8153
|
+
if (m.discriminator && m.fields.length === 0) {
|
|
8154
|
+
const original = originalByName.get(m.name);
|
|
8155
|
+
if (original && original.fields.length > 0) return {
|
|
8156
|
+
...m,
|
|
8157
|
+
fields: original.fields
|
|
8158
|
+
};
|
|
8159
|
+
}
|
|
8160
|
+
return m;
|
|
8161
|
+
});
|
|
8162
|
+
}
|
|
7665
8163
|
function withNodeOperationOverrides(ctx) {
|
|
7666
8164
|
const cached = contextCache.get(ctx);
|
|
7667
8165
|
if (cached) return cached;
|
|
8166
|
+
const enrichedModels = enrichSpecModels(ctx.spec.models);
|
|
8167
|
+
const specChanged = enrichedModels.length !== ctx.spec.models.length || enrichedModels.some((m, i) => m !== ctx.spec.models[i]);
|
|
8168
|
+
const enrichedSpec = specChanged ? {
|
|
8169
|
+
...ctx.spec,
|
|
8170
|
+
models: enrichedModels
|
|
8171
|
+
} : ctx.spec;
|
|
7668
8172
|
const resolvedOperations = ctx.resolvedOperations;
|
|
7669
8173
|
if (!resolvedOperations?.length) {
|
|
7670
|
-
|
|
7671
|
-
|
|
8174
|
+
const next = specChanged ? {
|
|
8175
|
+
...ctx,
|
|
8176
|
+
spec: enrichedSpec
|
|
8177
|
+
} : ctx;
|
|
8178
|
+
contextCache.set(ctx, next);
|
|
8179
|
+
return next;
|
|
7672
8180
|
}
|
|
7673
|
-
let
|
|
8181
|
+
let opsChanged = false;
|
|
7674
8182
|
const nextResolved = resolvedOperations.map((resolved) => {
|
|
7675
8183
|
const override = OPERATION_OVERRIDES[operationKey(resolved)];
|
|
7676
8184
|
if (!override) return resolved;
|
|
7677
8185
|
const methodName = override.methodName ?? resolved.methodName;
|
|
7678
8186
|
const mountOn = override.mountOn ?? resolved.mountOn;
|
|
7679
8187
|
if (methodName === resolved.methodName && mountOn === resolved.mountOn) return resolved;
|
|
7680
|
-
|
|
8188
|
+
opsChanged = true;
|
|
7681
8189
|
return {
|
|
7682
8190
|
...resolved,
|
|
7683
8191
|
methodName,
|
|
7684
8192
|
mountOn
|
|
7685
8193
|
};
|
|
7686
8194
|
});
|
|
7687
|
-
const next =
|
|
8195
|
+
const next = opsChanged || specChanged ? {
|
|
7688
8196
|
...ctx,
|
|
7689
|
-
resolvedOperations: nextResolved
|
|
8197
|
+
...opsChanged ? { resolvedOperations: nextResolved } : {},
|
|
8198
|
+
...specChanged ? { spec: enrichedSpec } : {}
|
|
7690
8199
|
} : ctx;
|
|
7691
8200
|
contextCache.set(ctx, next);
|
|
7692
8201
|
return next;
|
|
@@ -7698,6 +8207,22 @@ function withNodeOperationOverrides(ctx) {
|
|
|
7698
8207
|
* one oagen run, so we walk the target SDK once and reuse it.
|
|
7699
8208
|
*/
|
|
7700
8209
|
const surfaceCache = /* @__PURE__ */ new WeakMap();
|
|
8210
|
+
/**
|
|
8211
|
+
* Paths the node emitter has produced so far in this ctx, accumulated across
|
|
8212
|
+
* `applyLiveSurface` calls. Drives `carryForwardManagedFiles` so files in the
|
|
8213
|
+
* prior manifest that we did not re-emit this run still land in the new
|
|
8214
|
+
* manifest as "still managed" — without that, the orchestrator's prune diff
|
|
8215
|
+
* treats every untouched autogen file as stale.
|
|
8216
|
+
*/
|
|
8217
|
+
const emittedPathsCache = /* @__PURE__ */ new WeakMap();
|
|
8218
|
+
function getEmittedPaths(ctx) {
|
|
8219
|
+
let set = emittedPathsCache.get(ctx);
|
|
8220
|
+
if (!set) {
|
|
8221
|
+
set = /* @__PURE__ */ new Set();
|
|
8222
|
+
emittedPathsCache.set(ctx, set);
|
|
8223
|
+
}
|
|
8224
|
+
return set;
|
|
8225
|
+
}
|
|
7701
8226
|
function getSurface(ctx) {
|
|
7702
8227
|
let surface = surfaceCache.get(ctx);
|
|
7703
8228
|
if (surface) return surface;
|
|
@@ -7744,8 +8269,10 @@ function getSurface(ctx) {
|
|
|
7744
8269
|
* `integrateTarget: false` files (smoke-manifest.json etc.) are also dropped:
|
|
7745
8270
|
* with no `--target` step they would otherwise land as untracked cruft.
|
|
7746
8271
|
*
|
|
7747
|
-
* Note:
|
|
7748
|
-
*
|
|
8272
|
+
* Note: the carry-forward step in `generateTests` re-declares prior-manifest
|
|
8273
|
+
* paths we didn't touch this run, so the orchestrator's prune diff stays
|
|
8274
|
+
* accurate without needing `--no-prune` at the call site. See
|
|
8275
|
+
* `carryForwardManagedFiles` below.
|
|
7749
8276
|
*/
|
|
7750
8277
|
/**
|
|
7751
8278
|
* `*.spec.ts`, `*.test.ts`, and JSON fixtures under `fixtures/` are owned by
|
|
@@ -7895,19 +8422,99 @@ function applyLiveSurface(files, ctx, surface) {
|
|
|
7895
8422
|
if (f.content && !f.content.endsWith("\n")) f.content += "\n";
|
|
7896
8423
|
out.push(f);
|
|
7897
8424
|
}
|
|
8425
|
+
const emitted = getEmittedPaths(ctx);
|
|
8426
|
+
for (const f of out) emitted.add(f.path);
|
|
8427
|
+
return out;
|
|
8428
|
+
}
|
|
8429
|
+
/**
|
|
8430
|
+
* Re-declare prior-manifest paths that we did not emit this run so manifest
|
|
8431
|
+
* pruning can tell "intentionally removed" from "untouched but still managed."
|
|
8432
|
+
*
|
|
8433
|
+
* The node emitter only outputs files it actually wants to write each run —
|
|
8434
|
+
* untouched-but-up-to-date autogen files don't come back through any
|
|
8435
|
+
* `generateXxx` method. Without this carry-forward, the orchestrator's
|
|
8436
|
+
* `prevManifest.files − currentEmission` diff treats every such file as stale
|
|
8437
|
+
* and prunes the whole tree on a regen. That's why `scripts/sdk-generate.sh`
|
|
8438
|
+
* historically paired the node emitter with `--no-prune` — at the cost of
|
|
8439
|
+
* never pruning legitimately-removed files (e.g. an enum file orphaned by a
|
|
8440
|
+
* `schemaNameTransform` rename like `RadarAction` → `RadarListAction`).
|
|
8441
|
+
*
|
|
8442
|
+
* The carry-forward entry uses `skipIfExists: true`, so writer.ts skips the
|
|
8443
|
+
* write and only ensures the header is present (no-op for files that already
|
|
8444
|
+
* have it). The path still lands in `outputEmittedPaths` and therefore in the
|
|
8445
|
+
* new manifest, which restores correct prune semantics.
|
|
8446
|
+
*
|
|
8447
|
+
* Files dropped from the carry-forward set:
|
|
8448
|
+
* - Not on disk anymore (file was hand-deleted — let prune confirm absence).
|
|
8449
|
+
* - `@oagen-ignore-file` protected (user has explicitly taken ownership).
|
|
8450
|
+
* - `.ts` files that no longer carry the auto-gen header (user has taken
|
|
8451
|
+
* ownership in-place; the next prune cycle will clear the manifest entry).
|
|
8452
|
+
*/
|
|
8453
|
+
function carryForwardManagedFiles(ctx, surface) {
|
|
8454
|
+
const priorPaths = ctx.priorTargetManifestPaths;
|
|
8455
|
+
if (!priorPaths || priorPaths.size === 0) return [];
|
|
8456
|
+
const emitted = getEmittedPaths(ctx);
|
|
8457
|
+
const out = [];
|
|
8458
|
+
for (const relPath of priorPaths) {
|
|
8459
|
+
if (emitted.has(relPath)) continue;
|
|
8460
|
+
if (!surface.files.has(relPath)) continue;
|
|
8461
|
+
if (surface.protectedFiles.has(relPath)) continue;
|
|
8462
|
+
if (relPath.endsWith(".ts") && !surface.autogenFiles.has(relPath)) continue;
|
|
8463
|
+
out.push({
|
|
8464
|
+
path: relPath,
|
|
8465
|
+
content: "",
|
|
8466
|
+
skipIfExists: true,
|
|
8467
|
+
headerPlacement: "skip"
|
|
8468
|
+
});
|
|
8469
|
+
emitted.add(relPath);
|
|
8470
|
+
}
|
|
7898
8471
|
return out;
|
|
7899
8472
|
}
|
|
8473
|
+
/**
|
|
8474
|
+
* Flatten oneOf / allOf+oneOf variant fields from the raw spec onto each
|
|
8475
|
+
* model. `enrichModelsFromSpec` produces (a) extra optional fields on models
|
|
8476
|
+
* whose schema is `allOf [base, oneOf [...]]`, and (b) synthetic models /
|
|
8477
|
+
* enums for inline objects encountered inside variants (e.g. the inline
|
|
8478
|
+
* `redirect_uris` item shape on `ConnectApplication`).
|
|
8479
|
+
*
|
|
8480
|
+
* Node, like Go / Kotlin / .NET, emits flat interfaces rather than a sum
|
|
8481
|
+
* type, so on `enrichModelsFromSpec`-marked discriminated bases we restore
|
|
8482
|
+
* the original IR fields — otherwise the base interface would be empty.
|
|
8483
|
+
* A future change can emit a real TS discriminated union; for now the goal
|
|
8484
|
+
* is parity with the other flat-emit languages so every variant field is
|
|
8485
|
+
* at least reachable.
|
|
8486
|
+
*/
|
|
8487
|
+
function enrichModelsForNode(models) {
|
|
8488
|
+
const enriched = enrichModelsFromSpec(models);
|
|
8489
|
+
const originalByName = new Map(models.map((m) => [m.name, m]));
|
|
8490
|
+
return enriched.map((m) => {
|
|
8491
|
+
if (m.discriminator && m.fields.length === 0) {
|
|
8492
|
+
const original = originalByName.get(m.name);
|
|
8493
|
+
if (original && original.fields.length > 0) return {
|
|
8494
|
+
...m,
|
|
8495
|
+
fields: original.fields
|
|
8496
|
+
};
|
|
8497
|
+
}
|
|
8498
|
+
return m;
|
|
8499
|
+
});
|
|
8500
|
+
}
|
|
7900
8501
|
const nodeEmitter = {
|
|
7901
8502
|
language: "node",
|
|
7902
8503
|
generateModels(models, ctx) {
|
|
7903
8504
|
const nodeCtx = withNodeOperationOverrides(ctx);
|
|
7904
8505
|
const surface = getSurface(nodeCtx);
|
|
7905
|
-
|
|
8506
|
+
const enriched = enrichModelsForNode(models);
|
|
8507
|
+
const discPlans = planDiscriminatedModels(enriched, nodeCtx);
|
|
8508
|
+
nodeCtx._discriminatedModelNames = new Set(discPlans.keys());
|
|
8509
|
+
const standardFiles = generateModelsAndSerializers(enriched, nodeCtx);
|
|
8510
|
+
const discFiles = generateDiscriminatedFiles(discPlans, nodeCtx);
|
|
8511
|
+
return applyLiveSurface([...standardFiles, ...discFiles], nodeCtx, surface);
|
|
7906
8512
|
},
|
|
7907
8513
|
generateEnums(enums, ctx) {
|
|
7908
8514
|
const nodeCtx = withNodeOperationOverrides(ctx);
|
|
7909
8515
|
const surface = getSurface(nodeCtx);
|
|
7910
|
-
|
|
8516
|
+
const syntheticEnums = getSyntheticEnums();
|
|
8517
|
+
return applyLiveSurface(generateEnums$7([...enums, ...syntheticEnums], nodeCtx), nodeCtx, surface);
|
|
7911
8518
|
},
|
|
7912
8519
|
generateResources(services, ctx) {
|
|
7913
8520
|
const nodeCtx = withNodeOperationOverrides(ctx);
|
|
@@ -7917,7 +8524,7 @@ const nodeEmitter = {
|
|
|
7917
8524
|
generateClient(spec, ctx) {
|
|
7918
8525
|
const nodeCtx = withNodeOperationOverrides(ctx);
|
|
7919
8526
|
const surface = getSurface(nodeCtx);
|
|
7920
|
-
return applyLiveSurface(generateClient$7(spec, nodeCtx), nodeCtx, surface);
|
|
8527
|
+
return applyLiveSurface(generateClient$7(nodeCtx.spec, nodeCtx), nodeCtx, surface);
|
|
7921
8528
|
},
|
|
7922
8529
|
generateErrors(_ctx) {
|
|
7923
8530
|
return [];
|
|
@@ -7927,9 +8534,8 @@ const nodeEmitter = {
|
|
|
7927
8534
|
},
|
|
7928
8535
|
generateTests(spec, ctx) {
|
|
7929
8536
|
const nodeCtx = withNodeOperationOverrides(ctx);
|
|
7930
|
-
if (!nodeOptions(nodeCtx).regenerateOwnedTests) return [];
|
|
7931
8537
|
const surface = getSurface(nodeCtx);
|
|
7932
|
-
return applyLiveSurface(generateTests$7(spec, nodeCtx), nodeCtx, surface);
|
|
8538
|
+
return [...nodeOptions(nodeCtx).regenerateOwnedTests ? applyLiveSurface(generateTests$7(spec, nodeCtx), nodeCtx, surface) : [], ...carryForwardManagedFiles(nodeCtx, surface)];
|
|
7933
8539
|
},
|
|
7934
8540
|
buildOperationsMap() {
|
|
7935
8541
|
return {};
|
|
@@ -8706,8 +9312,9 @@ function generateModels$6(models, ctx) {
|
|
|
8706
9312
|
const symbolToFile = /* @__PURE__ */ new Map();
|
|
8707
9313
|
const symbolToOriginalService = /* @__PURE__ */ new Map();
|
|
8708
9314
|
const emittedFilePaths = /* @__PURE__ */ new Set();
|
|
9315
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
8709
9316
|
for (const model of models) {
|
|
8710
|
-
if (isListWrapperModel(model)) continue;
|
|
9317
|
+
if (isListWrapperModel(model) && !nonPaginatedRefs.has(model.name)) continue;
|
|
8711
9318
|
if (isListMetadataModel(model)) continue;
|
|
8712
9319
|
const dirName = resolveDir(modelToService.get(model.name));
|
|
8713
9320
|
const modelClassName = className$5(model.name);
|
|
@@ -10121,7 +10728,9 @@ function generateResources$6(services, ctx) {
|
|
|
10121
10728
|
}
|
|
10122
10729
|
for (const op of allOperations) {
|
|
10123
10730
|
const plan = planOperation(op);
|
|
10124
|
-
if (plan.responseModelName
|
|
10731
|
+
if (plan.responseModelName) {
|
|
10732
|
+
if (!listWrapperNames.has(plan.responseModelName) || !plan.isPaginated) modelImports.add(plan.responseModelName);
|
|
10733
|
+
}
|
|
10125
10734
|
if (op.requestBody?.kind === "model") {
|
|
10126
10735
|
const requestBodyRef = op.requestBody;
|
|
10127
10736
|
modelImports.add(requestBodyRef.name);
|
|
@@ -12250,9 +12859,10 @@ function generateModels$5(models, ctx) {
|
|
|
12250
12859
|
].join("\n"),
|
|
12251
12860
|
overwriteExisting: true
|
|
12252
12861
|
});
|
|
12862
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
12253
12863
|
for (const model of models) {
|
|
12254
12864
|
if (isListMetadataModel(model)) continue;
|
|
12255
|
-
if (isListWrapperModel(model)) continue;
|
|
12865
|
+
if (isListWrapperModel(model) && !nonPaginatedRefs.has(model.name)) continue;
|
|
12256
12866
|
const name = className$4(model.name);
|
|
12257
12867
|
const lines = [];
|
|
12258
12868
|
lines.push(`namespace ${ctx.namespacePascal}\\Resource;`);
|
|
@@ -13845,15 +14455,37 @@ function ensureTrailingNewlines$5(files) {
|
|
|
13845
14455
|
for (const f of files) if (f.content && !f.content.endsWith("\n")) f.content += "\n";
|
|
13846
14456
|
return files;
|
|
13847
14457
|
}
|
|
14458
|
+
/**
|
|
14459
|
+
* Flatten oneOf / allOf+oneOf variant fields onto each base model and pull
|
|
14460
|
+
* in synthetic models / enums for inline variant shapes. PHP emits flat
|
|
14461
|
+
* classes (no sum types), so a discriminated base whose IR fields the
|
|
14462
|
+
* parser stripped (post-allOf-aware detection) gets its original fields
|
|
14463
|
+
* restored to avoid silently dropping variant data.
|
|
14464
|
+
*/
|
|
14465
|
+
function enrichModelsForPhp(models) {
|
|
14466
|
+
const enriched = enrichModelsFromSpec(models);
|
|
14467
|
+
const originalByName = new Map(models.map((m) => [m.name, m]));
|
|
14468
|
+
return enriched.map((m) => {
|
|
14469
|
+
if (m.discriminator && m.fields.length === 0) {
|
|
14470
|
+
const original = originalByName.get(m.name);
|
|
14471
|
+
if (original && original.fields.length > 0) return {
|
|
14472
|
+
...m,
|
|
14473
|
+
fields: original.fields
|
|
14474
|
+
};
|
|
14475
|
+
}
|
|
14476
|
+
return m;
|
|
14477
|
+
});
|
|
14478
|
+
}
|
|
13848
14479
|
const phpEmitter = {
|
|
13849
14480
|
language: "php",
|
|
13850
14481
|
generateModels(models, ctx) {
|
|
13851
14482
|
ensureNamingInitialized(ctx);
|
|
13852
|
-
return ensureTrailingNewlines$5(generateModels$5(models, ctx));
|
|
14483
|
+
return ensureTrailingNewlines$5(generateModels$5(enrichModelsForPhp(models), ctx));
|
|
13853
14484
|
},
|
|
13854
14485
|
generateEnums(enums, ctx) {
|
|
13855
14486
|
ensureNamingInitialized(ctx);
|
|
13856
|
-
|
|
14487
|
+
const syntheticEnums = getSyntheticEnums();
|
|
14488
|
+
return ensureTrailingNewlines$5(generateEnums$5([...enums, ...syntheticEnums], ctx));
|
|
13857
14489
|
},
|
|
13858
14490
|
generateResources(services, ctx) {
|
|
13859
14491
|
ensureNamingInitialized(ctx);
|
|
@@ -14130,10 +14762,12 @@ function generateModels$4(models, ctx) {
|
|
|
14130
14762
|
lines.push(`package ${ctx.namespace}`);
|
|
14131
14763
|
lines.push("");
|
|
14132
14764
|
const requestBodyOnly = collectRequestBodyOnlyModelNames$1(ctx.spec.services, models);
|
|
14765
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
14766
|
+
const skipAsListWrapper = (m) => isListWrapperModel(m) && !nonPaginatedRefs.has(m.name);
|
|
14133
14767
|
const modelHashMap = /* @__PURE__ */ new Map();
|
|
14134
14768
|
const hashGroups = /* @__PURE__ */ new Map();
|
|
14135
14769
|
for (const model of models) {
|
|
14136
|
-
if (
|
|
14770
|
+
if (skipAsListWrapper(model) || isListMetadataModel(model)) continue;
|
|
14137
14771
|
if (requestBodyOnly.has(model.name)) continue;
|
|
14138
14772
|
const hash = structuralHash$2(model);
|
|
14139
14773
|
modelHashMap.set(model.name, hash);
|
|
@@ -14150,7 +14784,7 @@ function generateModels$4(models, ctx) {
|
|
|
14150
14784
|
}
|
|
14151
14785
|
const batchedAliases = /* @__PURE__ */ new Set();
|
|
14152
14786
|
for (const model of models) {
|
|
14153
|
-
if (
|
|
14787
|
+
if (skipAsListWrapper(model) || isListMetadataModel(model)) continue;
|
|
14154
14788
|
if (requestBodyOnly.has(model.name)) continue;
|
|
14155
14789
|
const structName = className$3(model.name);
|
|
14156
14790
|
const canonicalName = aliasOf.get(model.name);
|
|
@@ -16783,7 +17417,15 @@ function isEnumRef(ref) {
|
|
|
16783
17417
|
* omission so the API returns a clear `missing required field` error instead
|
|
16784
17418
|
* of a confusing 422.
|
|
16785
17419
|
*/
|
|
16786
|
-
function emitJsonPropertyAttributes(
|
|
17420
|
+
function emitJsonPropertyAttributes(wireName, options = {}) {
|
|
17421
|
+
if (options.explicitWireName) {
|
|
17422
|
+
if (options.isRequiredEnum) return [
|
|
17423
|
+
` [JsonProperty("${wireName}", DefaultValueHandling = DefaultValueHandling.Ignore)]`,
|
|
17424
|
+
` [STJS.JsonIgnore(Condition = STJS.JsonIgnoreCondition.WhenWritingDefault)]`,
|
|
17425
|
+
` [STJS.JsonPropertyName("${wireName}")]`
|
|
17426
|
+
];
|
|
17427
|
+
return [` [JsonProperty("${wireName}")]`, ` [STJS.JsonPropertyName("${wireName}")]`];
|
|
17428
|
+
}
|
|
16787
17429
|
if (options.isRequiredEnum) return [` [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]`, ` [STJS.JsonIgnore(Condition = STJS.JsonIgnoreCondition.WhenWritingDefault)]`];
|
|
16788
17430
|
return [];
|
|
16789
17431
|
}
|
|
@@ -16836,16 +17478,23 @@ function generateModels$3(models, ctx, discCtx) {
|
|
|
16836
17478
|
const files = [];
|
|
16837
17479
|
primeModelAliases(models);
|
|
16838
17480
|
const requestBodyOnlyNames = collectRequestBodyOnlyModelNames(ctx.spec.services, models);
|
|
17481
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
17482
|
+
const skipAsListWrapper = (m) => isListWrapperModel(m) && !nonPaginatedRefs.has(m.name);
|
|
16839
17483
|
const baseFieldLookup = /* @__PURE__ */ new Map();
|
|
16840
17484
|
if (discCtx) {
|
|
16841
17485
|
for (const model of models) if (discCtx.discriminatorBases.has(model.name)) {
|
|
17486
|
+
const baseClassName = modelClassName(model.name);
|
|
16842
17487
|
const fieldMap = /* @__PURE__ */ new Map();
|
|
16843
|
-
for (const field of model.fields)
|
|
17488
|
+
for (const field of model.fields) {
|
|
17489
|
+
let csName = fieldName$2(field.name);
|
|
17490
|
+
if (csName === baseClassName) csName = `${csName}Value`;
|
|
17491
|
+
fieldMap.set(csName, mapTypeRef$3(field.type));
|
|
17492
|
+
}
|
|
16844
17493
|
baseFieldLookup.set(model.name, fieldMap);
|
|
16845
17494
|
}
|
|
16846
17495
|
}
|
|
16847
17496
|
for (const model of models) {
|
|
16848
|
-
if (
|
|
17497
|
+
if (skipAsListWrapper(model) || isListMetadataModel(model)) continue;
|
|
16849
17498
|
if (requestBodyOnlyNames.has(model.name)) continue;
|
|
16850
17499
|
const csClassName = modelClassName(model.name);
|
|
16851
17500
|
if (isModelAlias(model.name)) continue;
|
|
@@ -16853,7 +17502,7 @@ function generateModels$3(models, ctx, discCtx) {
|
|
|
16853
17502
|
const fieldTypes = model.fields.map((f) => mapTypeRef$3(f.type));
|
|
16854
17503
|
const needsCollections = fieldTypes.some((t) => t.startsWith("List<") || t.startsWith("Dictionary<"));
|
|
16855
17504
|
const needsSystem = fieldTypes.some((t) => t.includes("DateTimeOffset"));
|
|
16856
|
-
const needsJsonAttrs = model.fields.some((f) => f.required && isEnumRef(f.type));
|
|
17505
|
+
const needsJsonAttrs = model.fields.some((f) => fieldName$2(f.name) === csClassName) || model.fields.some((f) => f.required && isEnumRef(f.type));
|
|
16857
17506
|
lines.push(`namespace ${ctx.namespacePascal}`);
|
|
16858
17507
|
lines.push("{");
|
|
16859
17508
|
if (needsSystem) lines.push(" using System;");
|
|
@@ -16879,7 +17528,9 @@ function generateModels$3(models, ctx, discCtx) {
|
|
|
16879
17528
|
const dictObjectFields = [];
|
|
16880
17529
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
16881
17530
|
for (const field of model.fields) {
|
|
16882
|
-
|
|
17531
|
+
let csFieldName = fieldName$2(field.name);
|
|
17532
|
+
const collidesWithClassName = csFieldName === csClassName;
|
|
17533
|
+
if (collidesWithClassName) csFieldName = `${csFieldName}Value`;
|
|
16883
17534
|
if (seenFieldNames.has(csFieldName)) continue;
|
|
16884
17535
|
seenFieldNames.add(csFieldName);
|
|
16885
17536
|
let useNewModifier = false;
|
|
@@ -16924,7 +17575,10 @@ function generateModels$3(models, ctx, discCtx) {
|
|
|
16924
17575
|
lines.push(` [System.Obsolete("${msg}")]`);
|
|
16925
17576
|
}
|
|
16926
17577
|
const isRequiredEnum = field.required && isEnumRef(field.type) && constInit === null;
|
|
16927
|
-
lines.push(...emitJsonPropertyAttributes(field.name, {
|
|
17578
|
+
lines.push(...emitJsonPropertyAttributes(field.name, {
|
|
17579
|
+
isRequiredEnum,
|
|
17580
|
+
explicitWireName: collidesWithClassName
|
|
17581
|
+
}));
|
|
16928
17582
|
const discriminatedUnionConverter = discriminatedUnionConverterName(field.type);
|
|
16929
17583
|
if (discriminatedUnionConverter) lines.push(` [Newtonsoft.Json.JsonConverter(typeof(${discriminatedUnionConverter}))]`);
|
|
16930
17584
|
const newMod = useNewModifier ? "new " : "";
|
|
@@ -19509,14 +20163,16 @@ function promoteFieldType$1(f) {
|
|
|
19509
20163
|
* List wrappers (`{ data, list_metadata }`) and the shared `ListMetadata`
|
|
19510
20164
|
* model are skipped — the hand-maintained runtime provides [Page]/[ListMetadata].
|
|
19511
20165
|
*/
|
|
19512
|
-
function generateModels$2(models,
|
|
20166
|
+
function generateModels$2(models, ctx) {
|
|
19513
20167
|
if (models.length === 0) return [];
|
|
19514
20168
|
for (const model of models) for (const field of model.fields) mapTypeRef$2(field.type);
|
|
19515
20169
|
const files = [];
|
|
20170
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
20171
|
+
const skipAsListWrapper = (m) => isListWrapperModel(m) && !nonPaginatedRefs.has(m.name);
|
|
19516
20172
|
modelAliasMap = null;
|
|
19517
20173
|
const hashGroupsPass1 = /* @__PURE__ */ new Map();
|
|
19518
20174
|
for (const model of models) {
|
|
19519
|
-
if (
|
|
20175
|
+
if (skipAsListWrapper(model) || isListMetadataModel(model)) continue;
|
|
19520
20176
|
if (model.fields.length === 0 && discriminatedUnions.has(className$1(model.name))) continue;
|
|
19521
20177
|
const hash = structuralHash(model);
|
|
19522
20178
|
if (!hashGroupsPass1.has(hash)) hashGroupsPass1.set(hash, []);
|
|
@@ -19535,7 +20191,7 @@ function generateModels$2(models, _ctx) {
|
|
|
19535
20191
|
modelAliasMap = aliasOf;
|
|
19536
20192
|
const hashGroupsPass2 = /* @__PURE__ */ new Map();
|
|
19537
20193
|
for (const model of models) {
|
|
19538
|
-
if (
|
|
20194
|
+
if (skipAsListWrapper(model) || isListMetadataModel(model)) continue;
|
|
19539
20195
|
if (model.fields.length === 0 && discriminatedUnions.has(className$1(model.name))) continue;
|
|
19540
20196
|
if (aliasOf.has(model.name)) continue;
|
|
19541
20197
|
const hash = structuralHash(model);
|
|
@@ -19553,7 +20209,7 @@ function generateModels$2(models, _ctx) {
|
|
|
19553
20209
|
}
|
|
19554
20210
|
}
|
|
19555
20211
|
for (const model of models) {
|
|
19556
|
-
if (
|
|
20212
|
+
if (skipAsListWrapper(model) || isListMetadataModel(model)) continue;
|
|
19557
20213
|
const typeName = className$1(model.name);
|
|
19558
20214
|
if (model.fields.length === 0 && discriminatedUnions.has(typeName)) {
|
|
19559
20215
|
files.push(emitSealedUnion(typeName, discriminatedUnions.get(typeName)));
|
|
@@ -19581,7 +20237,7 @@ function generateModels$2(models, _ctx) {
|
|
|
19581
20237
|
}
|
|
19582
20238
|
const eventMapping = [];
|
|
19583
20239
|
for (const model of models) {
|
|
19584
|
-
if (
|
|
20240
|
+
if (skipAsListWrapper(model) || isListMetadataModel(model)) continue;
|
|
19585
20241
|
if (aliasOf.has(model.name)) continue;
|
|
19586
20242
|
if (!isEventEnvelopeModel(model)) continue;
|
|
19587
20243
|
const eventField = model.fields.find((f) => f.name === "event");
|
|
@@ -22298,10 +22954,12 @@ function generateModels$1(models, ctx) {
|
|
|
22298
22954
|
return mountDirMap.get(service) ?? classifyUnassignedModel(modelName);
|
|
22299
22955
|
};
|
|
22300
22956
|
const files = [];
|
|
22957
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
22958
|
+
const skipAsListWrapper = (m) => isListWrapperModel(m) && !nonPaginatedRefs.has(m.name);
|
|
22301
22959
|
const recursiveHashes = buildRecursiveHashMap(models, enumNames);
|
|
22302
22960
|
const hashGroups = /* @__PURE__ */ new Map();
|
|
22303
22961
|
for (const m of models) {
|
|
22304
|
-
if (
|
|
22962
|
+
if (skipAsListWrapper(m) || isListMetadataModel(m)) continue;
|
|
22305
22963
|
const h = recursiveHashes.get(m.name) ?? "";
|
|
22306
22964
|
if (!hashGroups.has(h)) hashGroups.set(h, []);
|
|
22307
22965
|
hashGroups.get(h).push(m.name);
|
|
@@ -22314,7 +22972,7 @@ function generateModels$1(models, ctx) {
|
|
|
22314
22972
|
for (let i = 1; i < sorted.length; i++) aliasOf.set(sorted[i], canonical);
|
|
22315
22973
|
}
|
|
22316
22974
|
for (const model of models) {
|
|
22317
|
-
if (
|
|
22975
|
+
if (skipAsListWrapper(model) || isListMetadataModel(model)) continue;
|
|
22318
22976
|
const cls = className(model.name);
|
|
22319
22977
|
const file = fileName(model.name);
|
|
22320
22978
|
const canonical = aliasOf.get(model.name);
|
|
@@ -24436,13 +25094,37 @@ function ensureTrailingNewlines$1(files) {
|
|
|
24436
25094
|
for (const f of files) if (f.content && !f.content.endsWith("\n")) f.content += "\n";
|
|
24437
25095
|
return files;
|
|
24438
25096
|
}
|
|
25097
|
+
/**
|
|
25098
|
+
* Flatten oneOf / allOf+oneOf variant fields onto each base model and pick
|
|
25099
|
+
* up the synthetic models / enums `enrichModelsFromSpec` produces for inline
|
|
25100
|
+
* variant shapes. Ruby emits flat hash-backed models, not sum types, so a
|
|
25101
|
+
* discriminated base whose IR fields were stripped (the new EventSchema-
|
|
25102
|
+
* style behaviour after the parser learned to walk allOf-wrapped variants)
|
|
25103
|
+
* has its original fields restored — otherwise `ConnectApplication`-style
|
|
25104
|
+
* bases would silently lose every variant field they had previously.
|
|
25105
|
+
*/
|
|
25106
|
+
function enrichModelsForRuby(models, enums) {
|
|
25107
|
+
const enriched = enrichModelsFromSpec(models, enums);
|
|
25108
|
+
const originalByName = new Map(models.map((m) => [m.name, m]));
|
|
25109
|
+
return enriched.map((m) => {
|
|
25110
|
+
if (m.discriminator && m.fields.length === 0) {
|
|
25111
|
+
const original = originalByName.get(m.name);
|
|
25112
|
+
if (original && original.fields.length > 0) return {
|
|
25113
|
+
...m,
|
|
25114
|
+
fields: original.fields
|
|
25115
|
+
};
|
|
25116
|
+
}
|
|
25117
|
+
return m;
|
|
25118
|
+
});
|
|
25119
|
+
}
|
|
24439
25120
|
const rubyEmitter = {
|
|
24440
25121
|
language: "ruby",
|
|
24441
25122
|
generateModels(models, ctx) {
|
|
24442
|
-
return ensureTrailingNewlines$1(generateModels$1(models, ctx));
|
|
25123
|
+
return ensureTrailingNewlines$1(generateModels$1(enrichModelsForRuby(models, ctx.spec.enums), ctx));
|
|
24443
25124
|
},
|
|
24444
25125
|
generateEnums(enums, ctx) {
|
|
24445
|
-
|
|
25126
|
+
const syntheticEnums = getSyntheticEnums();
|
|
25127
|
+
return ensureTrailingNewlines$1(generateEnums$1([...enums, ...syntheticEnums], ctx));
|
|
24446
25128
|
},
|
|
24447
25129
|
generateResources(services, ctx) {
|
|
24448
25130
|
return ensureTrailingNewlines$1(generateResources$1(services, ctx));
|
|
@@ -24904,7 +25586,7 @@ function renderField(field, rustField, modelName, registry) {
|
|
|
24904
25586
|
function renderModelsBarrel(modules) {
|
|
24905
25587
|
const sorted = [...new Set(modules)].sort();
|
|
24906
25588
|
const lines = [];
|
|
24907
|
-
for (const m of sorted) lines.push(`
|
|
25589
|
+
for (const m of sorted) lines.push(`mod ${m};`);
|
|
24908
25590
|
lines.push("");
|
|
24909
25591
|
for (const m of sorted) lines.push(`pub use ${m}::*;`);
|
|
24910
25592
|
return lines.join("\n") + "\n";
|
|
@@ -25987,7 +26669,7 @@ function renderResourcesBarrel(exports) {
|
|
|
25987
26669
|
}
|
|
25988
26670
|
unique.sort((a, b) => a.module.localeCompare(b.module));
|
|
25989
26671
|
const lines = [];
|
|
25990
|
-
for (const { module } of unique) lines.push(`
|
|
26672
|
+
for (const { module } of unique) lines.push(`mod ${module};`);
|
|
25991
26673
|
lines.push("");
|
|
25992
26674
|
for (const { module, struct } of unique) lines.push(`pub use ${module}::${struct};`);
|
|
25993
26675
|
return lines.join("\n") + "\n";
|
|
@@ -26731,14 +27413,37 @@ function ensureTrailingNewlines(files) {
|
|
|
26731
27413
|
for (const f of files) if (f.content && !f.content.endsWith("\n")) f.content += "\n";
|
|
26732
27414
|
return files;
|
|
26733
27415
|
}
|
|
27416
|
+
/**
|
|
27417
|
+
* Flatten oneOf / allOf+oneOf variant fields onto each base model and pull
|
|
27418
|
+
* in synthetic models / enums for inline variant shapes. Rust emits flat
|
|
27419
|
+
* structs (a synthesised enum-union from `UnionRegistry` exists, but the
|
|
27420
|
+
* field-on-base pattern is what matches `ConnectApplication` today). A
|
|
27421
|
+
* discriminated base whose IR fields the parser stripped gets its original
|
|
27422
|
+
* fields restored to avoid losing variant data.
|
|
27423
|
+
*/
|
|
27424
|
+
function enrichModelsForRust(models) {
|
|
27425
|
+
const enriched = enrichModelsFromSpec(models);
|
|
27426
|
+
const originalByName = new Map(models.map((m) => [m.name, m]));
|
|
27427
|
+
return enriched.map((m) => {
|
|
27428
|
+
if (m.discriminator && m.fields.length === 0) {
|
|
27429
|
+
const original = originalByName.get(m.name);
|
|
27430
|
+
if (original && original.fields.length > 0) return {
|
|
27431
|
+
...m,
|
|
27432
|
+
fields: original.fields
|
|
27433
|
+
};
|
|
27434
|
+
}
|
|
27435
|
+
return m;
|
|
27436
|
+
});
|
|
27437
|
+
}
|
|
26734
27438
|
const rustEmitter = {
|
|
26735
27439
|
language: "rust",
|
|
26736
27440
|
generateModels(models, ctx) {
|
|
26737
27441
|
unionRegistry.reset();
|
|
26738
|
-
return ensureTrailingNewlines(generateModels(models, ctx, unionRegistry));
|
|
27442
|
+
return ensureTrailingNewlines(generateModels(enrichModelsForRust(models), ctx, unionRegistry));
|
|
26739
27443
|
},
|
|
26740
27444
|
generateEnums(enums, ctx) {
|
|
26741
|
-
|
|
27445
|
+
const syntheticEnums = getSyntheticEnums();
|
|
27446
|
+
return ensureTrailingNewlines(generateEnums([...enums, ...syntheticEnums], ctx));
|
|
26742
27447
|
},
|
|
26743
27448
|
generateResources(services, ctx) {
|
|
26744
27449
|
return ensureTrailingNewlines(generateResources(services, ctx, unionRegistry));
|
|
@@ -26814,4 +27519,4 @@ const workosEmittersPlugin = {
|
|
|
26814
27519
|
//#endregion
|
|
26815
27520
|
export { pythonEmitter as _, rustExtractor as a, pythonExtractor as c, rustEmitter as d, rubyEmitter as f, phpEmitter as g, goEmitter as h, kotlinExtractor as i, rubyExtractor as l, dotnetEmitter as m, elixirExtractor as n, goExtractor as o, kotlinEmitter as p, dotnetExtractor as r, phpExtractor as s, workosEmittersPlugin as t, nodeExtractor as u, nodeEmitter as v };
|
|
26816
27521
|
|
|
26817
|
-
//# sourceMappingURL=plugin-
|
|
27522
|
+
//# sourceMappingURL=plugin-DRGwxN88.mjs.map
|