@workos/oagen-emitters 0.9.1 → 0.11.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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +23 -0
- package/dist/index.mjs +1 -1
- package/dist/{plugin-Dh9JSScr.mjs → plugin-DW3cnedr.mjs} +570 -211
- package/dist/plugin-DW3cnedr.mjs.map +1 -0
- package/dist/plugin.mjs +1 -1
- package/package.json +2 -2
- package/src/dotnet/enums.ts +37 -11
- package/src/go/models.ts +48 -3
- package/src/php/models.ts +27 -3
- package/src/php/resources.ts +16 -16
- package/src/python/enums.ts +11 -54
- package/src/python/models.ts +204 -219
- package/src/python/resources.ts +19 -35
- package/src/python/shared-schemas.ts +488 -0
- package/src/python/tests.ts +9 -7
- package/src/ruby/resources.ts +13 -1
- package/test/dotnet/enums.test.ts +146 -0
- package/test/go/models.test.ts +116 -1
- package/test/go/resources.test.ts +70 -0
- package/test/php/models.test.ts +77 -0
- package/test/php/resources.test.ts +95 -0
- package/test/python/enums.test.ts +91 -0
- package/test/python/models.test.ts +225 -0
- package/test/python/resources.test.ts +45 -0
- package/test/ruby/resources.test.ts +58 -0
- package/dist/plugin-Dh9JSScr.mjs.map +0 -1
|
@@ -3245,7 +3245,7 @@ function computeNonEventReachable(services, models) {
|
|
|
3245
3245
|
//#region src/node/enums.ts
|
|
3246
3246
|
function generateEnums$6(enums, ctx) {
|
|
3247
3247
|
if (enums.length === 0) return [];
|
|
3248
|
-
const enumToService = assignEnumsToServices
|
|
3248
|
+
const enumToService = assignEnumsToServices(enums, ctx.spec.services);
|
|
3249
3249
|
const serviceNameMap = buildServiceNameMap(ctx.spec.services, ctx);
|
|
3250
3250
|
const resolveDir = (irService) => irService ? resolveServiceDir$1(serviceNameMap.get(irService) ?? irService) : "common";
|
|
3251
3251
|
const files = [];
|
|
@@ -3315,7 +3315,7 @@ function extractLiteralUnionValues(aliasValue) {
|
|
|
3315
3315
|
while ((match = regex.exec(aliasValue)) !== null) values.add(match[1]);
|
|
3316
3316
|
return values;
|
|
3317
3317
|
}
|
|
3318
|
-
function assignEnumsToServices
|
|
3318
|
+
function assignEnumsToServices(enums, services) {
|
|
3319
3319
|
const enumToService = /* @__PURE__ */ new Map();
|
|
3320
3320
|
const enumNames = new Set(enums.map((e) => e.name));
|
|
3321
3321
|
for (const service of services) for (const op of service.operations) {
|
|
@@ -3855,7 +3855,7 @@ function generateModels$6(models, ctx, shared) {
|
|
|
3855
3855
|
const typeDecls = /* @__PURE__ */ new Map();
|
|
3856
3856
|
const crossServiceImports = /* @__PURE__ */ new Map();
|
|
3857
3857
|
const unresolvableNames = /* @__PURE__ */ new Set();
|
|
3858
|
-
const enumToService = assignEnumsToServices
|
|
3858
|
+
const enumToService = assignEnumsToServices(ctx.spec.enums, ctx.spec.services);
|
|
3859
3859
|
const resolvedEnumNames = /* @__PURE__ */ new Map();
|
|
3860
3860
|
for (const e of ctx.spec.enums) resolvedEnumNames.set(resolveInterfaceName(e.name, ctx), e.name);
|
|
3861
3861
|
for (const field of model.fields) {
|
|
@@ -4960,7 +4960,7 @@ function generateResourceClass(service, ctx) {
|
|
|
4960
4960
|
for (const [relPath, specifiers] of serializerImportsByPath) lines.push(`import { ${specifiers.join(", ")} } from '${relPath}';`);
|
|
4961
4961
|
const specEnumNames = new Set(ctx.spec.enums.map((e) => e.name));
|
|
4962
4962
|
if (paramEnums.size > 0) {
|
|
4963
|
-
const enumToService = assignEnumsToServices
|
|
4963
|
+
const enumToService = assignEnumsToServices(ctx.spec.enums, ctx.spec.services);
|
|
4964
4964
|
for (const name of paramEnums) {
|
|
4965
4965
|
if (allModels.has(name)) continue;
|
|
4966
4966
|
if (!specEnumNames.has(name)) continue;
|
|
@@ -6924,6 +6924,330 @@ function joinUnionVariants$4(ref, variants) {
|
|
|
6924
6924
|
return `Union[${unique.join(", ")}]`;
|
|
6925
6925
|
}
|
|
6926
6926
|
//#endregion
|
|
6927
|
+
//#region src/python/shared-schemas.ts
|
|
6928
|
+
/**
|
|
6929
|
+
* Walk every operation across all services and tally, per schema, the set of
|
|
6930
|
+
* services that transitively reference it. Schemas referenced by more than one
|
|
6931
|
+
* service are "shared" — they should be emitted under common/ rather than
|
|
6932
|
+
* the first alphabetical service that happens to use them.
|
|
6933
|
+
*
|
|
6934
|
+
* Transitive walk for models follows model->model field references AND
|
|
6935
|
+
* discriminator variant mappings to a fixed point; enums are leaves.
|
|
6936
|
+
*/
|
|
6937
|
+
function findSharedSchemas(spec) {
|
|
6938
|
+
const modelsByName = new Map(spec.models.map((m) => [m.name, m]));
|
|
6939
|
+
const modelToServices = /* @__PURE__ */ new Map();
|
|
6940
|
+
const enumToServices = /* @__PURE__ */ new Map();
|
|
6941
|
+
const note = (map, name, service) => {
|
|
6942
|
+
let bucket = map.get(name);
|
|
6943
|
+
if (!bucket) {
|
|
6944
|
+
bucket = /* @__PURE__ */ new Set();
|
|
6945
|
+
map.set(name, bucket);
|
|
6946
|
+
}
|
|
6947
|
+
bucket.add(service);
|
|
6948
|
+
};
|
|
6949
|
+
for (const service of spec.services) {
|
|
6950
|
+
const directModels = /* @__PURE__ */ new Set();
|
|
6951
|
+
const directEnums = /* @__PURE__ */ new Set();
|
|
6952
|
+
const collect = (ref) => {
|
|
6953
|
+
walkTypeRef(ref, {
|
|
6954
|
+
model: (r) => directModels.add(r.name),
|
|
6955
|
+
enum: (r) => directEnums.add(r.name)
|
|
6956
|
+
});
|
|
6957
|
+
};
|
|
6958
|
+
for (const op of service.operations) {
|
|
6959
|
+
if (op.requestBody) collect(op.requestBody);
|
|
6960
|
+
collect(op.response);
|
|
6961
|
+
for (const p of [
|
|
6962
|
+
...op.pathParams,
|
|
6963
|
+
...op.queryParams,
|
|
6964
|
+
...op.headerParams,
|
|
6965
|
+
...op.cookieParams ?? []
|
|
6966
|
+
]) collect(p.type);
|
|
6967
|
+
if (op.pagination) collect(op.pagination.itemType);
|
|
6968
|
+
for (const err of op.errors) if (err.type) collect(err.type);
|
|
6969
|
+
for (const sr of op.successResponses ?? []) collect(sr.type);
|
|
6970
|
+
}
|
|
6971
|
+
const queue = [...directModels];
|
|
6972
|
+
while (queue.length > 0) {
|
|
6973
|
+
const name = queue.pop();
|
|
6974
|
+
const model = modelsByName.get(name);
|
|
6975
|
+
if (!model) continue;
|
|
6976
|
+
for (const field of model.fields) walkTypeRef(field.type, {
|
|
6977
|
+
model: (r) => {
|
|
6978
|
+
if (!directModels.has(r.name)) {
|
|
6979
|
+
directModels.add(r.name);
|
|
6980
|
+
queue.push(r.name);
|
|
6981
|
+
}
|
|
6982
|
+
},
|
|
6983
|
+
enum: (r) => directEnums.add(r.name)
|
|
6984
|
+
});
|
|
6985
|
+
const disc = model.discriminator;
|
|
6986
|
+
if (disc?.mapping) {
|
|
6987
|
+
for (const variantName of Object.values(disc.mapping)) if (!directModels.has(variantName)) {
|
|
6988
|
+
directModels.add(variantName);
|
|
6989
|
+
queue.push(variantName);
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
}
|
|
6993
|
+
for (const name of directModels) note(modelToServices, name, service.name);
|
|
6994
|
+
for (const name of directEnums) note(enumToServices, name, service.name);
|
|
6995
|
+
}
|
|
6996
|
+
const sharedModels = /* @__PURE__ */ new Set();
|
|
6997
|
+
for (const [name, services] of modelToServices) if (services.size >= 2) sharedModels.add(name);
|
|
6998
|
+
const sharedEnums = /* @__PURE__ */ new Set();
|
|
6999
|
+
for (const [name, services] of enumToServices) if (services.size >= 2) sharedEnums.add(name);
|
|
7000
|
+
return {
|
|
7001
|
+
models: sharedModels,
|
|
7002
|
+
enums: sharedEnums
|
|
7003
|
+
};
|
|
7004
|
+
}
|
|
7005
|
+
function computeSchemaPlacement(spec, ctx) {
|
|
7006
|
+
const annotatedModels = detectDiscriminators(spec.models);
|
|
7007
|
+
if (annotatedModels !== spec.models) spec = {
|
|
7008
|
+
...spec,
|
|
7009
|
+
models: annotatedModels
|
|
7010
|
+
};
|
|
7011
|
+
const modelsByName = new Map(spec.models.map((m) => [m.name, m]));
|
|
7012
|
+
const hintedModels = new Set(Object.keys(ctx.modelHints ?? {}));
|
|
7013
|
+
const originalModelToService = assignModelsToServices(spec.models, spec.services, ctx.modelHints);
|
|
7014
|
+
const originalEnumToService = assignEnumsToServicesNatural(spec.enums, spec.services);
|
|
7015
|
+
const modelAliases = computeModelAliases(spec);
|
|
7016
|
+
const enumAliases = computeEnumAliases(spec.enums);
|
|
7017
|
+
const initial = findSharedSchemas(spec);
|
|
7018
|
+
for (const [aliasName, canonicalName] of modelAliases) if (initial.models.has(aliasName)) initial.models.add(canonicalName);
|
|
7019
|
+
for (const [aliasName, canonicalName] of enumAliases) if (initial.enums.has(aliasName)) initial.enums.add(canonicalName);
|
|
7020
|
+
const sharedModels = /* @__PURE__ */ new Set();
|
|
7021
|
+
for (const name of initial.models) if (!hintedModels.has(name)) sharedModels.add(name);
|
|
7022
|
+
for (const model of spec.models) if (!originalModelToService.has(model.name) && !hintedModels.has(model.name)) sharedModels.add(model.name);
|
|
7023
|
+
const sharedEnums = new Set(initial.enums);
|
|
7024
|
+
for (const enumDef of spec.enums) if (!originalEnumToService.has(enumDef.name)) sharedEnums.add(enumDef.name);
|
|
7025
|
+
let changed = true;
|
|
7026
|
+
while (changed) {
|
|
7027
|
+
changed = false;
|
|
7028
|
+
for (const name of sharedModels) {
|
|
7029
|
+
const model = modelsByName.get(name);
|
|
7030
|
+
if (!model) continue;
|
|
7031
|
+
const deps = collectEmittedDependencies(model, modelAliases);
|
|
7032
|
+
for (const dep of deps.models) {
|
|
7033
|
+
if (sharedModels.has(dep)) continue;
|
|
7034
|
+
if (!modelsByName.has(dep)) continue;
|
|
7035
|
+
sharedModels.add(dep);
|
|
7036
|
+
changed = true;
|
|
7037
|
+
}
|
|
7038
|
+
for (const dep of deps.enums) {
|
|
7039
|
+
if (sharedEnums.has(dep)) continue;
|
|
7040
|
+
sharedEnums.add(dep);
|
|
7041
|
+
changed = true;
|
|
7042
|
+
}
|
|
7043
|
+
}
|
|
7044
|
+
}
|
|
7045
|
+
const modelToService = new Map(originalModelToService);
|
|
7046
|
+
for (const name of sharedModels) modelToService.delete(name);
|
|
7047
|
+
const enumToService = new Map(originalEnumToService);
|
|
7048
|
+
for (const name of sharedEnums) enumToService.delete(name);
|
|
7049
|
+
const relocatedModels = /* @__PURE__ */ new Set();
|
|
7050
|
+
for (const name of sharedModels) {
|
|
7051
|
+
if (!originalModelToService.has(name)) continue;
|
|
7052
|
+
relocatedModels.add(name);
|
|
7053
|
+
}
|
|
7054
|
+
const relocatedEnums = /* @__PURE__ */ new Set();
|
|
7055
|
+
for (const name of sharedEnums) {
|
|
7056
|
+
if (!originalEnumToService.has(name)) continue;
|
|
7057
|
+
relocatedEnums.add(name);
|
|
7058
|
+
}
|
|
7059
|
+
return {
|
|
7060
|
+
modelToService,
|
|
7061
|
+
enumToService,
|
|
7062
|
+
originalModelToService,
|
|
7063
|
+
originalEnumToService,
|
|
7064
|
+
relocatedModels,
|
|
7065
|
+
relocatedEnums,
|
|
7066
|
+
modelAliases,
|
|
7067
|
+
enumAliases
|
|
7068
|
+
};
|
|
7069
|
+
}
|
|
7070
|
+
/**
|
|
7071
|
+
* Dependencies the emitter will materialize for a model's generated file.
|
|
7072
|
+
* Captures alias canonicals, discriminator variants, and field-level model+enum
|
|
7073
|
+
* references so the placement closure can decide whether the dependency must
|
|
7074
|
+
* also live in `common/`.
|
|
7075
|
+
*/
|
|
7076
|
+
function collectEmittedDependencies(model, modelAliases) {
|
|
7077
|
+
const models = /* @__PURE__ */ new Set();
|
|
7078
|
+
const enums = /* @__PURE__ */ new Set();
|
|
7079
|
+
const canonical = modelAliases.get(model.name);
|
|
7080
|
+
if (canonical) {
|
|
7081
|
+
models.add(canonical);
|
|
7082
|
+
return {
|
|
7083
|
+
models,
|
|
7084
|
+
enums
|
|
7085
|
+
};
|
|
7086
|
+
}
|
|
7087
|
+
const disc = model.discriminator;
|
|
7088
|
+
if (disc?.mapping) for (const variant of Object.values(disc.mapping)) models.add(variant);
|
|
7089
|
+
const fieldDeps = collectFieldDependencies(model);
|
|
7090
|
+
for (const m of fieldDeps.models) models.add(m);
|
|
7091
|
+
for (const e of fieldDeps.enums) enums.add(e);
|
|
7092
|
+
return {
|
|
7093
|
+
models,
|
|
7094
|
+
enums
|
|
7095
|
+
};
|
|
7096
|
+
}
|
|
7097
|
+
function collectModelUsage(spec) {
|
|
7098
|
+
const request = /* @__PURE__ */ new Set();
|
|
7099
|
+
const response = /* @__PURE__ */ new Set();
|
|
7100
|
+
for (const service of spec.services) for (const op of service.operations) {
|
|
7101
|
+
const plan = planOperation(op);
|
|
7102
|
+
if (plan.responseModelName) response.add(plan.responseModelName);
|
|
7103
|
+
if (op.pagination?.itemType.kind === "model") response.add(op.pagination.itemType.name);
|
|
7104
|
+
if (op.requestBody?.kind === "model") request.add(op.requestBody.name);
|
|
7105
|
+
if (op.requestBody?.kind === "union") {
|
|
7106
|
+
for (const variant of op.requestBody.variants ?? []) if (variant.kind === "model") request.add(variant.name);
|
|
7107
|
+
}
|
|
7108
|
+
}
|
|
7109
|
+
const mixed = /* @__PURE__ */ new Set();
|
|
7110
|
+
for (const name of request) if (response.has(name)) mixed.add(name);
|
|
7111
|
+
return {
|
|
7112
|
+
requestOnly: new Set([...request].filter((name) => !mixed.has(name))),
|
|
7113
|
+
response: new Set([...response].filter((name) => !mixed.has(name))),
|
|
7114
|
+
mixed
|
|
7115
|
+
};
|
|
7116
|
+
}
|
|
7117
|
+
function compareAliasPriority(left, right, usage) {
|
|
7118
|
+
const score = (name) => {
|
|
7119
|
+
if (usage.response.has(name)) return 0;
|
|
7120
|
+
if (usage.mixed.has(name)) return 1;
|
|
7121
|
+
if (usage.requestOnly.has(name)) return 2;
|
|
7122
|
+
return 3;
|
|
7123
|
+
};
|
|
7124
|
+
const diff = score(left) - score(right);
|
|
7125
|
+
if (diff !== 0) return diff;
|
|
7126
|
+
return left.localeCompare(right);
|
|
7127
|
+
}
|
|
7128
|
+
function canAliasModels(canonical, alias, usage) {
|
|
7129
|
+
if (fileName$2(canonical) === fileName$2(alias)) return false;
|
|
7130
|
+
if (usage.response.has(canonical) && usage.requestOnly.has(alias) || usage.response.has(alias) && usage.requestOnly.has(canonical)) return false;
|
|
7131
|
+
return true;
|
|
7132
|
+
}
|
|
7133
|
+
/**
|
|
7134
|
+
* Compute the Python emitter's structural model dedup map: alias -> canonical.
|
|
7135
|
+
* Mirrors the logic in models.ts so the placement closure can promote
|
|
7136
|
+
* canonicals when their aliases are shared.
|
|
7137
|
+
*/
|
|
7138
|
+
function computeModelAliases(spec) {
|
|
7139
|
+
const recursiveHashes = buildRecursiveHashMap$1(spec.models, spec.enums);
|
|
7140
|
+
const usage = collectModelUsage(spec);
|
|
7141
|
+
const hashGroups = /* @__PURE__ */ new Map();
|
|
7142
|
+
for (const model of spec.models) {
|
|
7143
|
+
if (model.fields.length === 0 && !model.discriminator) {}
|
|
7144
|
+
const hash = recursiveHashes.get(model.name) ?? "";
|
|
7145
|
+
if (!hashGroups.has(hash)) hashGroups.set(hash, []);
|
|
7146
|
+
hashGroups.get(hash).push(model.name);
|
|
7147
|
+
}
|
|
7148
|
+
const aliasOf = /* @__PURE__ */ new Map();
|
|
7149
|
+
for (const [, names] of hashGroups) {
|
|
7150
|
+
if (names.length <= 1) continue;
|
|
7151
|
+
const sorted = [...names].sort((a, b) => compareAliasPriority(a, b, usage));
|
|
7152
|
+
const canonical = sorted[0];
|
|
7153
|
+
for (let i = 1; i < sorted.length; i++) if (canAliasModels(canonical, sorted[i], usage)) aliasOf.set(sorted[i], canonical);
|
|
7154
|
+
}
|
|
7155
|
+
return aliasOf;
|
|
7156
|
+
}
|
|
7157
|
+
/**
|
|
7158
|
+
* Compute the Python emitter's structural enum dedup map: alias -> canonical.
|
|
7159
|
+
* Mirrors the logic in enums.ts.
|
|
7160
|
+
*/
|
|
7161
|
+
function computeEnumAliases(enums) {
|
|
7162
|
+
const hashGroups = /* @__PURE__ */ new Map();
|
|
7163
|
+
for (const enumDef of enums) {
|
|
7164
|
+
const hash = [...enumDef.values].map((v) => String(v.value)).sort().join("|");
|
|
7165
|
+
if (!hashGroups.has(hash)) hashGroups.set(hash, []);
|
|
7166
|
+
hashGroups.get(hash).push(enumDef.name);
|
|
7167
|
+
}
|
|
7168
|
+
const aliasOf = /* @__PURE__ */ new Map();
|
|
7169
|
+
for (const [, names] of hashGroups) {
|
|
7170
|
+
if (names.length <= 1) continue;
|
|
7171
|
+
const sorted = [...names].sort();
|
|
7172
|
+
const canonical = sorted[0];
|
|
7173
|
+
for (let i = 1; i < sorted.length; i++) aliasOf.set(sorted[i], canonical);
|
|
7174
|
+
}
|
|
7175
|
+
return aliasOf;
|
|
7176
|
+
}
|
|
7177
|
+
/**
|
|
7178
|
+
* Recursive structural hashing for models so dedup runs against deeply-equal
|
|
7179
|
+
* shapes, not just same-named ones. Cycles fall back to the model name.
|
|
7180
|
+
*/
|
|
7181
|
+
function buildRecursiveHashMap$1(models, enums) {
|
|
7182
|
+
const modelByName = new Map(models.map((m) => [m.name, m]));
|
|
7183
|
+
const hashCache = /* @__PURE__ */ new Map();
|
|
7184
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
7185
|
+
const enumVH = /* @__PURE__ */ new Map();
|
|
7186
|
+
for (const e of enums) enumVH.set(e.name, [...e.values].map((v) => String(v.value)).sort().join("|"));
|
|
7187
|
+
function modelHash(name) {
|
|
7188
|
+
const cached = hashCache.get(name);
|
|
7189
|
+
if (cached != null) return cached;
|
|
7190
|
+
if (visiting.has(name)) return `m:${name}`;
|
|
7191
|
+
visiting.add(name);
|
|
7192
|
+
const model = modelByName.get(name);
|
|
7193
|
+
if (!model) {
|
|
7194
|
+
visiting.delete(name);
|
|
7195
|
+
return `m:${name}`;
|
|
7196
|
+
}
|
|
7197
|
+
const hash = [...model.fields].sort((a, b) => a.name.localeCompare(b.name)).map((f) => `${f.name}:${deepTypeHash(f.type)}:${f.required}`).join("|");
|
|
7198
|
+
visiting.delete(name);
|
|
7199
|
+
hashCache.set(name, hash);
|
|
7200
|
+
return hash;
|
|
7201
|
+
}
|
|
7202
|
+
function deepTypeHash(ref) {
|
|
7203
|
+
switch (ref.kind) {
|
|
7204
|
+
case "primitive": return `p:${ref.type}${ref.format ? `:${ref.format}` : ""}`;
|
|
7205
|
+
case "model": return `m:{${modelHash(ref.name)}}`;
|
|
7206
|
+
case "enum": {
|
|
7207
|
+
const vh = enumVH.get(ref.name);
|
|
7208
|
+
return vh != null ? `e:{${vh}}` : `e:${ref.name}`;
|
|
7209
|
+
}
|
|
7210
|
+
case "array": return `a:${deepTypeHash(ref.items)}`;
|
|
7211
|
+
case "nullable": return `n:${deepTypeHash(ref.inner)}`;
|
|
7212
|
+
case "union": return `u:${(ref.variants ?? []).map((v) => deepTypeHash(v)).sort().join(",")}`;
|
|
7213
|
+
case "map": return `d:${deepTypeHash(ref.valueType)}`;
|
|
7214
|
+
case "literal": return `l:${String(ref.value)}`;
|
|
7215
|
+
default: return "unknown";
|
|
7216
|
+
}
|
|
7217
|
+
}
|
|
7218
|
+
for (const model of models) modelHash(model.name);
|
|
7219
|
+
return hashCache;
|
|
7220
|
+
}
|
|
7221
|
+
/**
|
|
7222
|
+
* Natural enum-to-service assignment without sharing logic — the first service
|
|
7223
|
+
* (alphabetically by spec order) to reference an enum wins.
|
|
7224
|
+
*/
|
|
7225
|
+
function assignEnumsToServicesNatural(enums, services) {
|
|
7226
|
+
const enumNames = new Set(enums.map((e) => e.name));
|
|
7227
|
+
const enumToService = /* @__PURE__ */ new Map();
|
|
7228
|
+
for (const service of services) {
|
|
7229
|
+
const refs = /* @__PURE__ */ new Set();
|
|
7230
|
+
const collect = (ref) => {
|
|
7231
|
+
walkTypeRef(ref, { enum: (r) => refs.add(r.name) });
|
|
7232
|
+
};
|
|
7233
|
+
for (const op of service.operations) {
|
|
7234
|
+
if (op.requestBody) collect(op.requestBody);
|
|
7235
|
+
collect(op.response);
|
|
7236
|
+
for (const p of [
|
|
7237
|
+
...op.pathParams,
|
|
7238
|
+
...op.queryParams,
|
|
7239
|
+
...op.headerParams,
|
|
7240
|
+
...op.cookieParams ?? []
|
|
7241
|
+
]) collect(p.type);
|
|
7242
|
+
}
|
|
7243
|
+
for (const name of refs) {
|
|
7244
|
+
if (!enumNames.has(name)) continue;
|
|
7245
|
+
if (!enumToService.has(name)) enumToService.set(name, service.name);
|
|
7246
|
+
}
|
|
7247
|
+
}
|
|
7248
|
+
return enumToService;
|
|
7249
|
+
}
|
|
7250
|
+
//#endregion
|
|
6927
7251
|
//#region src/python/enums.ts
|
|
6928
7252
|
/**
|
|
6929
7253
|
* Convert a PascalCase class name to a human-readable lowercase string,
|
|
@@ -6940,12 +7264,16 @@ function humanizeClassName(name) {
|
|
|
6940
7264
|
*/
|
|
6941
7265
|
function generateEnums$5(enums, ctx) {
|
|
6942
7266
|
if (enums.length === 0) return [];
|
|
6943
|
-
const
|
|
7267
|
+
const placement = computeSchemaPlacement(enums === ctx.spec.enums ? ctx.spec : {
|
|
7268
|
+
...ctx.spec,
|
|
7269
|
+
enums
|
|
7270
|
+
}, ctx);
|
|
7271
|
+
const enumToService = placement.enumToService;
|
|
6944
7272
|
const mountDirMap = buildMountDirMap$1(ctx);
|
|
6945
7273
|
const resolveDir = (irService) => irService ? mountDirMap.get(irService) ?? "common" : "common";
|
|
6946
7274
|
const files = [];
|
|
6947
7275
|
const compatAliases = collectCompatEnumAliases(enums, ctx);
|
|
6948
|
-
const aliasOf =
|
|
7276
|
+
const aliasOf = placement.enumAliases;
|
|
6949
7277
|
for (const enumDef of enums) {
|
|
6950
7278
|
const dirName = resolveDir(enumToService.get(enumDef.name));
|
|
6951
7279
|
const canonicalName = aliasOf.get(enumDef.name);
|
|
@@ -7110,24 +7438,11 @@ function collectCompatEnumAliases(enums, ctx) {
|
|
|
7110
7438
|
}
|
|
7111
7439
|
return aliases;
|
|
7112
7440
|
}
|
|
7113
|
-
function collectEnumAliasOf$3(enums) {
|
|
7114
|
-
const hashGroups = /* @__PURE__ */ new Map();
|
|
7115
|
-
for (const enumDef of enums) {
|
|
7116
|
-
const hash = [...enumDef.values].map((v) => String(v.value)).sort().join("|");
|
|
7117
|
-
if (!hashGroups.has(hash)) hashGroups.set(hash, []);
|
|
7118
|
-
hashGroups.get(hash).push(enumDef.name);
|
|
7119
|
-
}
|
|
7120
|
-
const aliasOf = /* @__PURE__ */ new Map();
|
|
7121
|
-
for (const [, names] of hashGroups) {
|
|
7122
|
-
if (names.length <= 1) continue;
|
|
7123
|
-
const sorted = [...names].sort();
|
|
7124
|
-
const canonical = sorted[0];
|
|
7125
|
-
for (let i = 1; i < sorted.length; i++) aliasOf.set(sorted[i], canonical);
|
|
7126
|
-
}
|
|
7127
|
-
return aliasOf;
|
|
7128
|
-
}
|
|
7129
7441
|
function collectGeneratedEnumSymbolsByDir(enums, ctx) {
|
|
7130
|
-
const enumToService =
|
|
7442
|
+
const enumToService = computeSchemaPlacement(enums === ctx.spec.enums ? ctx.spec : {
|
|
7443
|
+
...ctx.spec,
|
|
7444
|
+
enums
|
|
7445
|
+
}, ctx).enumToService;
|
|
7131
7446
|
const mountDirMap = buildMountDirMap$1(ctx);
|
|
7132
7447
|
const resolveDir = (irService) => irService ? mountDirMap.get(irService) ?? "common" : "common";
|
|
7133
7448
|
const compatAliases = collectCompatEnumAliases(enums, ctx);
|
|
@@ -7143,26 +7458,6 @@ function collectGeneratedEnumSymbolsByDir(enums, ctx) {
|
|
|
7143
7458
|
function enumValueHash(enumDef) {
|
|
7144
7459
|
return [...enumDef.values].map((value) => String(value.value)).sort().join("|");
|
|
7145
7460
|
}
|
|
7146
|
-
function assignEnumsToServices(enums, services) {
|
|
7147
|
-
const enumToService = /* @__PURE__ */ new Map();
|
|
7148
|
-
const enumNames = new Set(enums.map((e) => e.name));
|
|
7149
|
-
for (const service of services) for (const op of service.operations) {
|
|
7150
|
-
const refs = /* @__PURE__ */ new Set();
|
|
7151
|
-
const collect = (ref) => {
|
|
7152
|
-
walkTypeRef(ref, { enum: (r) => refs.add(r.name) });
|
|
7153
|
-
};
|
|
7154
|
-
if (op.requestBody) collect(op.requestBody);
|
|
7155
|
-
collect(op.response);
|
|
7156
|
-
for (const p of [
|
|
7157
|
-
...op.pathParams,
|
|
7158
|
-
...op.queryParams,
|
|
7159
|
-
...op.headerParams,
|
|
7160
|
-
...op.cookieParams ?? []
|
|
7161
|
-
]) collect(p.type);
|
|
7162
|
-
for (const name of refs) if (enumNames.has(name) && !enumToService.has(name)) enumToService.set(name, service.name);
|
|
7163
|
-
}
|
|
7164
|
-
return enumToService;
|
|
7165
|
-
}
|
|
7166
7461
|
//#endregion
|
|
7167
7462
|
//#region src/python/models.ts
|
|
7168
7463
|
/**
|
|
@@ -7171,29 +7466,16 @@ function assignEnumsToServices(enums, services) {
|
|
|
7171
7466
|
*/
|
|
7172
7467
|
function generateModels$5(models, ctx) {
|
|
7173
7468
|
if (models.length === 0) return [];
|
|
7174
|
-
const modelToService =
|
|
7175
|
-
|
|
7469
|
+
const { modelToService, enumToService, originalModelToService, originalEnumToService, relocatedModels, relocatedEnums, modelAliases: aliasOf } = computeSchemaPlacement(models === ctx.spec.models ? ctx.spec : {
|
|
7470
|
+
...ctx.spec,
|
|
7471
|
+
models
|
|
7472
|
+
}, ctx);
|
|
7176
7473
|
const mountDirMap = buildMountDirMap$1(ctx);
|
|
7177
7474
|
const resolveDir = (irService) => irService ? mountDirMap.get(irService) ?? "common" : "common";
|
|
7178
7475
|
const files = [];
|
|
7179
7476
|
const emittedModelSymbolsByDir = /* @__PURE__ */ new Map();
|
|
7180
7477
|
const symbolToFile = /* @__PURE__ */ new Map();
|
|
7181
|
-
const
|
|
7182
|
-
const recursiveHashes = buildRecursiveHashMap$1(models, ctx.spec.enums);
|
|
7183
|
-
const hashGroups = /* @__PURE__ */ new Map();
|
|
7184
|
-
for (const model of models) {
|
|
7185
|
-
if (isListWrapperModel(model) || isListMetadataModel(model)) continue;
|
|
7186
|
-
const hash = recursiveHashes.get(model.name) ?? "";
|
|
7187
|
-
if (!hashGroups.has(hash)) hashGroups.set(hash, []);
|
|
7188
|
-
hashGroups.get(hash).push(model.name);
|
|
7189
|
-
}
|
|
7190
|
-
const aliasOf = /* @__PURE__ */ new Map();
|
|
7191
|
-
for (const [, names] of hashGroups) {
|
|
7192
|
-
if (names.length <= 1) continue;
|
|
7193
|
-
const sorted = [...names].sort((a, b) => compareAliasPriority(a, b, modelUsage));
|
|
7194
|
-
const canonical = sorted[0];
|
|
7195
|
-
for (let i = 1; i < sorted.length; i++) if (canAliasModels(canonical, sorted[i], modelUsage)) aliasOf.set(sorted[i], canonical);
|
|
7196
|
-
}
|
|
7478
|
+
const symbolToOriginalService = /* @__PURE__ */ new Map();
|
|
7197
7479
|
const emittedFilePaths = /* @__PURE__ */ new Set();
|
|
7198
7480
|
for (const model of models) {
|
|
7199
7481
|
if (isListWrapperModel(model)) continue;
|
|
@@ -7282,6 +7564,12 @@ function generateModels$5(models, ctx) {
|
|
|
7282
7564
|
symbolToFile.set(variantTypeName, fileName$2(model.name));
|
|
7283
7565
|
emittedModelSymbolsByDir.get(dirName).push(unknownClassName);
|
|
7284
7566
|
symbolToFile.set(unknownClassName, fileName$2(model.name));
|
|
7567
|
+
const dispatcherNatural = originalModelToService.get(model.name);
|
|
7568
|
+
if (dispatcherNatural) {
|
|
7569
|
+
symbolToOriginalService.set(model.name, dispatcherNatural);
|
|
7570
|
+
symbolToOriginalService.set(variantTypeName, dispatcherNatural);
|
|
7571
|
+
symbolToOriginalService.set(unknownClassName, dispatcherNatural);
|
|
7572
|
+
}
|
|
7285
7573
|
continue;
|
|
7286
7574
|
}
|
|
7287
7575
|
const canonicalName = aliasOf.get(model.name);
|
|
@@ -7303,6 +7591,8 @@ function generateModels$5(models, ctx) {
|
|
|
7303
7591
|
});
|
|
7304
7592
|
if (!emittedModelSymbolsByDir.has(dirName)) emittedModelSymbolsByDir.set(dirName, []);
|
|
7305
7593
|
emittedModelSymbolsByDir.get(dirName).push(model.name);
|
|
7594
|
+
const aliasNatural = originalModelToService.get(model.name);
|
|
7595
|
+
if (aliasNatural) symbolToOriginalService.set(model.name, aliasNatural);
|
|
7306
7596
|
continue;
|
|
7307
7597
|
}
|
|
7308
7598
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
@@ -7387,19 +7677,29 @@ function generateModels$5(models, ctx) {
|
|
|
7387
7677
|
lines.push(` def from_dict(cls, data: Dict[str, Any]) -> "${modelClassName}":`);
|
|
7388
7678
|
lines.push(` """Deserialize from a dictionary."""`);
|
|
7389
7679
|
lines.push(" try:");
|
|
7390
|
-
|
|
7680
|
+
const preludeLines = [];
|
|
7681
|
+
const fieldAssignmentLines = [];
|
|
7391
7682
|
for (const field of [...requiredFields, ...optionalFields]) {
|
|
7392
7683
|
const pyFieldName = fieldName$4(field.name);
|
|
7393
7684
|
const wireKey = field.name;
|
|
7394
7685
|
const isRequired = !isOptionalField(model.name, field, ctx);
|
|
7686
|
+
const discPrelude = renderDiscriminatedUnionPrelude(field, pyFieldName, wireKey, modelClassName, isRequired);
|
|
7687
|
+
if (discPrelude) {
|
|
7688
|
+
preludeLines.push(...discPrelude.prelude);
|
|
7689
|
+
fieldAssignmentLines.push(` ${pyFieldName}=${discPrelude.expr},`);
|
|
7690
|
+
continue;
|
|
7691
|
+
}
|
|
7395
7692
|
let accessor;
|
|
7396
7693
|
if (field.type.kind === "literal" && isRequired) accessor = `data.get("${wireKey}", ${pythonLiteralDefault(field.type.value)})`;
|
|
7397
7694
|
else accessor = isRequired ? `data["${wireKey}"]` : `data.get("${wireKey}")`;
|
|
7398
7695
|
const deserRequired = isRequired && field.type.kind !== "nullable";
|
|
7399
7696
|
const walrusVar = `_v_${pyFieldName}`;
|
|
7400
7697
|
const deserExpr = deserializeField(field.type, accessor, deserRequired, walrusVar);
|
|
7401
|
-
|
|
7698
|
+
fieldAssignmentLines.push(` ${pyFieldName}=${deserExpr},`);
|
|
7402
7699
|
}
|
|
7700
|
+
for (const preludeLine of preludeLines) lines.push(preludeLine);
|
|
7701
|
+
lines.push(" return cls(");
|
|
7702
|
+
for (const assignment of fieldAssignmentLines) lines.push(assignment);
|
|
7403
7703
|
lines.push(" )");
|
|
7404
7704
|
lines.push(" except (KeyError, ValueError) as e:");
|
|
7405
7705
|
lines.push(` _raise_deserialize_error("${modelClassName}", e)`);
|
|
@@ -7437,20 +7737,45 @@ function generateModels$5(models, ctx) {
|
|
|
7437
7737
|
});
|
|
7438
7738
|
if (!emittedModelSymbolsByDir.has(dirName)) emittedModelSymbolsByDir.set(dirName, []);
|
|
7439
7739
|
emittedModelSymbolsByDir.get(dirName).push(model.name);
|
|
7740
|
+
const regularNatural = originalModelToService.get(model.name);
|
|
7741
|
+
if (regularNatural) symbolToOriginalService.set(model.name, regularNatural);
|
|
7440
7742
|
}
|
|
7441
7743
|
const symbolsByDir = /* @__PURE__ */ new Map();
|
|
7442
7744
|
for (const [dirName, names] of emittedModelSymbolsByDir) {
|
|
7443
7745
|
const key = `src/${ctx.namespace}/${dirName}/models`;
|
|
7444
7746
|
if (!symbolsByDir.has(key)) symbolsByDir.set(key, []);
|
|
7445
|
-
symbolsByDir.get(key).push(
|
|
7747
|
+
for (const name of names) symbolsByDir.get(key).push({ name });
|
|
7446
7748
|
}
|
|
7447
7749
|
const reachableEnumNames = collectReachableEnumNames(ctx);
|
|
7448
7750
|
const enumSymbolsByDir = collectGeneratedEnumSymbolsByDir(ctx.spec.enums.filter((enumDef) => reachableEnumNames.has(enumDef.name)), ctx);
|
|
7449
7751
|
for (const [dirName, names] of enumSymbolsByDir) {
|
|
7450
7752
|
const key = `src/${ctx.namespace}/${dirName}/models`;
|
|
7451
7753
|
if (!symbolsByDir.has(key)) symbolsByDir.set(key, []);
|
|
7452
|
-
symbolsByDir.get(key).push(
|
|
7453
|
-
}
|
|
7754
|
+
for (const name of names) symbolsByDir.get(key).push({ name });
|
|
7755
|
+
}
|
|
7756
|
+
const commonDirName = "common";
|
|
7757
|
+
const addReExport = (naturalService, name, sourceFile) => {
|
|
7758
|
+
if (!naturalService) return;
|
|
7759
|
+
const naturalDir = mountDirMap.get(naturalService) ?? naturalService;
|
|
7760
|
+
if (naturalDir === commonDirName) return;
|
|
7761
|
+
const key = `src/${ctx.namespace}/${naturalDir}/models`;
|
|
7762
|
+
if (!symbolsByDir.has(key)) symbolsByDir.set(key, []);
|
|
7763
|
+
symbolsByDir.get(key).push({
|
|
7764
|
+
name,
|
|
7765
|
+
reExport: {
|
|
7766
|
+
fromDir: commonDirName,
|
|
7767
|
+
file: sourceFile
|
|
7768
|
+
}
|
|
7769
|
+
});
|
|
7770
|
+
};
|
|
7771
|
+
for (const symbol of symbolToOriginalService.keys()) {
|
|
7772
|
+
const naturalService = symbolToOriginalService.get(symbol);
|
|
7773
|
+
const file = symbolToFile.get(symbol) ?? fileName$2(symbol);
|
|
7774
|
+
const primaryName = file === fileName$2(symbol) ? symbol : reverseLookupModelByFile(file, ctx);
|
|
7775
|
+
if (primaryName && !relocatedModels.has(primaryName)) continue;
|
|
7776
|
+
addReExport(naturalService, symbol, file);
|
|
7777
|
+
}
|
|
7778
|
+
for (const enumName of relocatedEnums) addReExport(originalEnumToService.get(enumName), enumName, fileName$2(enumName));
|
|
7454
7779
|
const serviceDirModelPaths = /* @__PURE__ */ new Set();
|
|
7455
7780
|
for (const service of ctx.spec.services) {
|
|
7456
7781
|
const dirName = mountDirMap.get(service.name) ?? resolveDir(service.name);
|
|
@@ -7462,12 +7787,21 @@ function generateModels$5(models, ctx) {
|
|
|
7462
7787
|
integrateTarget: true,
|
|
7463
7788
|
overwriteExisting: true
|
|
7464
7789
|
});
|
|
7465
|
-
for (const [dirPath,
|
|
7466
|
-
const
|
|
7790
|
+
for (const [dirPath, symbols] of symbolsByDir) {
|
|
7791
|
+
const seen = /* @__PURE__ */ new Map();
|
|
7792
|
+
for (const sym of symbols) {
|
|
7793
|
+
const existing = seen.get(sym.name);
|
|
7794
|
+
if (!existing || existing.reExport && !sym.reExport) seen.set(sym.name, sym);
|
|
7795
|
+
}
|
|
7796
|
+
const uniqueSymbols = [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
7467
7797
|
const importLines = [];
|
|
7468
|
-
for (const
|
|
7469
|
-
const
|
|
7470
|
-
importLines.push(`from .${
|
|
7798
|
+
for (const sym of uniqueSymbols) {
|
|
7799
|
+
const cls = className$5(sym.name);
|
|
7800
|
+
if (sym.reExport) importLines.push(`from ${ctx.namespace}.${dirToModule(sym.reExport.fromDir)}.models.${sym.reExport.file} import ${cls} as ${cls}`);
|
|
7801
|
+
else {
|
|
7802
|
+
const fileNameForSymbol = symbolToFile.get(sym.name) ?? fileName$2(sym.name);
|
|
7803
|
+
importLines.push(`from .${fileNameForSymbol} import ${cls} as ${cls}`);
|
|
7804
|
+
}
|
|
7471
7805
|
}
|
|
7472
7806
|
const imports = importLines.join("\n");
|
|
7473
7807
|
files.push({
|
|
@@ -7478,7 +7812,7 @@ function generateModels$5(models, ctx) {
|
|
|
7478
7812
|
});
|
|
7479
7813
|
if (!serviceDirModelPaths.has(dirPath)) {
|
|
7480
7814
|
const parentDir = dirPath.replace(/\/models$/, "");
|
|
7481
|
-
const reExports =
|
|
7815
|
+
const reExports = uniqueSymbols.map((sym) => `from .models import ${className$5(sym.name)} as ${className$5(sym.name)}`).join("\n");
|
|
7482
7816
|
files.push({
|
|
7483
7817
|
path: `${parentDir}/__init__.py`,
|
|
7484
7818
|
content: reExports,
|
|
@@ -7489,6 +7823,14 @@ function generateModels$5(models, ctx) {
|
|
|
7489
7823
|
}
|
|
7490
7824
|
return files;
|
|
7491
7825
|
}
|
|
7826
|
+
/**
|
|
7827
|
+
* Given a snake_case file name, return the IR model name that owns it.
|
|
7828
|
+
* Used to attribute dispatcher children (FooVariant, FooUnknown) to their
|
|
7829
|
+
* parent model when computing relocation re-exports.
|
|
7830
|
+
*/
|
|
7831
|
+
function reverseLookupModelByFile(file, ctx) {
|
|
7832
|
+
for (const m of ctx.spec.models) if (fileName$2(m.name) === file) return m.name;
|
|
7833
|
+
}
|
|
7492
7834
|
function collectTypingImports(ref, imports) {
|
|
7493
7835
|
switch (ref.kind) {
|
|
7494
7836
|
case "array":
|
|
@@ -7553,42 +7895,6 @@ function collectReachableEnumNames(ctx) {
|
|
|
7553
7895
|
}
|
|
7554
7896
|
return referencedEnums;
|
|
7555
7897
|
}
|
|
7556
|
-
function collectModelUsage(spec) {
|
|
7557
|
-
const request = /* @__PURE__ */ new Set();
|
|
7558
|
-
const response = /* @__PURE__ */ new Set();
|
|
7559
|
-
for (const service of spec.services) for (const op of service.operations) {
|
|
7560
|
-
const plan = planOperation(op);
|
|
7561
|
-
if (plan.responseModelName) response.add(plan.responseModelName);
|
|
7562
|
-
if (op.pagination?.itemType.kind === "model") response.add(op.pagination.itemType.name);
|
|
7563
|
-
if (op.requestBody?.kind === "model") request.add(op.requestBody.name);
|
|
7564
|
-
if (op.requestBody?.kind === "union") {
|
|
7565
|
-
for (const variant of op.requestBody.variants ?? []) if (variant.kind === "model") request.add(variant.name);
|
|
7566
|
-
}
|
|
7567
|
-
}
|
|
7568
|
-
const mixed = /* @__PURE__ */ new Set();
|
|
7569
|
-
for (const name of request) if (response.has(name)) mixed.add(name);
|
|
7570
|
-
return {
|
|
7571
|
-
requestOnly: new Set([...request].filter((name) => !mixed.has(name))),
|
|
7572
|
-
response: new Set([...response].filter((name) => !mixed.has(name))),
|
|
7573
|
-
mixed
|
|
7574
|
-
};
|
|
7575
|
-
}
|
|
7576
|
-
function compareAliasPriority(left, right, usage) {
|
|
7577
|
-
const score = (name) => {
|
|
7578
|
-
if (usage.response.has(name)) return 0;
|
|
7579
|
-
if (usage.mixed.has(name)) return 1;
|
|
7580
|
-
if (usage.requestOnly.has(name)) return 2;
|
|
7581
|
-
return 3;
|
|
7582
|
-
};
|
|
7583
|
-
const diff = score(left) - score(right);
|
|
7584
|
-
if (diff !== 0) return diff;
|
|
7585
|
-
return left.localeCompare(right);
|
|
7586
|
-
}
|
|
7587
|
-
function canAliasModels(canonical, alias, usage) {
|
|
7588
|
-
if (fileName$2(canonical) === fileName$2(alias)) return false;
|
|
7589
|
-
if (usage.response.has(canonical) && usage.requestOnly.has(alias) || usage.response.has(alias) && usage.requestOnly.has(canonical)) return false;
|
|
7590
|
-
return true;
|
|
7591
|
-
}
|
|
7592
7898
|
function isOptionalField(modelName, field, ctx) {
|
|
7593
7899
|
if (!field.required || field.deprecated) return true;
|
|
7594
7900
|
return false;
|
|
@@ -7609,6 +7915,71 @@ function isDateTimeType(ref) {
|
|
|
7609
7915
|
if (ref.kind === "nullable") return isDateTimeType(ref.inner);
|
|
7610
7916
|
return ref.kind === "primitive" && ref.type === "string" && ref.format === "date-time";
|
|
7611
7917
|
}
|
|
7918
|
+
/**
|
|
7919
|
+
* If `field` is a discriminated-union (or nullable-wrapped discriminated-union)
|
|
7920
|
+
* field, return prelude statements that perform strict dispatch and an
|
|
7921
|
+
* expression for the `cls(...)` call. Returns null otherwise.
|
|
7922
|
+
*
|
|
7923
|
+
* Strict dispatch means: an unknown discriminator value raises ValueError
|
|
7924
|
+
* naming the parent class, field, observed value, and valid options. The
|
|
7925
|
+
* caller's `try/except` block converts that into `_raise_deserialize_error`.
|
|
7926
|
+
*/
|
|
7927
|
+
function renderDiscriminatedUnionPrelude(field, pyFieldName, wireKey, parentClassName, isRequired) {
|
|
7928
|
+
let unionRef = null;
|
|
7929
|
+
let nullable = false;
|
|
7930
|
+
if (field.type.kind === "union" && field.type.discriminator?.mapping) unionRef = field.type;
|
|
7931
|
+
else if (field.type.kind === "nullable" && field.type.inner.kind === "union" && field.type.inner.discriminator?.mapping) {
|
|
7932
|
+
unionRef = field.type.inner;
|
|
7933
|
+
nullable = true;
|
|
7934
|
+
}
|
|
7935
|
+
if (!unionRef) return null;
|
|
7936
|
+
const mapping = unionRef.discriminator.mapping;
|
|
7937
|
+
const entries = Object.entries(mapping);
|
|
7938
|
+
if (entries.length === 0) return null;
|
|
7939
|
+
const discProp = unionRef.discriminator.property;
|
|
7940
|
+
const rawVar = `_${pyFieldName}_raw`;
|
|
7941
|
+
const dataVar = `_${pyFieldName}_data`;
|
|
7942
|
+
const typeVar = `_${pyFieldName}_disc`;
|
|
7943
|
+
const mapVar = `_${pyFieldName}_disc_map`;
|
|
7944
|
+
const clsVar = `_${pyFieldName}_cls`;
|
|
7945
|
+
const valueVar = `_${pyFieldName}_value`;
|
|
7946
|
+
const indent = " ";
|
|
7947
|
+
const dispatchBlock = (innerIndent) => {
|
|
7948
|
+
const lines = [];
|
|
7949
|
+
lines.push(`${innerIndent}${dataVar} = cast(Dict[str, Any], ${rawVar})`);
|
|
7950
|
+
lines.push(`${innerIndent}${typeVar} = cast(str, ${dataVar}.get("${discProp}"))`);
|
|
7951
|
+
lines.push(`${innerIndent}${mapVar}: Dict[str, Any] = {`);
|
|
7952
|
+
for (const [value, variantModelName] of entries) lines.push(`${innerIndent} "${value}": ${className$5(variantModelName)},`);
|
|
7953
|
+
lines.push(`${innerIndent}}`);
|
|
7954
|
+
lines.push(`${innerIndent}${clsVar} = ${mapVar}.get(${typeVar})`);
|
|
7955
|
+
lines.push(`${innerIndent}if ${clsVar} is None:`);
|
|
7956
|
+
lines.push(`${innerIndent} raise ValueError(`);
|
|
7957
|
+
lines.push(`${innerIndent} f"Unknown discriminator '${discProp}' for ${parentClassName}.${pyFieldName}: {${typeVar}!r}. "`);
|
|
7958
|
+
lines.push(`${innerIndent} f"Expected one of {sorted(${mapVar})}."`);
|
|
7959
|
+
lines.push(`${innerIndent} )`);
|
|
7960
|
+
return lines;
|
|
7961
|
+
};
|
|
7962
|
+
const prelude = [];
|
|
7963
|
+
if (isRequired && !nullable) {
|
|
7964
|
+
prelude.push(`${indent}${rawVar} = data["${wireKey}"]`);
|
|
7965
|
+
prelude.push(...dispatchBlock(indent));
|
|
7966
|
+
return {
|
|
7967
|
+
prelude,
|
|
7968
|
+
expr: `${clsVar}.from_dict(${dataVar})`
|
|
7969
|
+
};
|
|
7970
|
+
}
|
|
7971
|
+
const accessor = isRequired ? `data["${wireKey}"]` : `data.get("${wireKey}")`;
|
|
7972
|
+
prelude.push(`${indent}${rawVar} = ${accessor}`);
|
|
7973
|
+
prelude.push(`${indent}if ${rawVar} is None:`);
|
|
7974
|
+
prelude.push(`${indent} ${valueVar} = None`);
|
|
7975
|
+
prelude.push(`${indent}else:`);
|
|
7976
|
+
prelude.push(...dispatchBlock(indent + " "));
|
|
7977
|
+
prelude.push(`${indent} ${valueVar} = ${clsVar}.from_dict(${dataVar})`);
|
|
7978
|
+
return {
|
|
7979
|
+
prelude,
|
|
7980
|
+
expr: valueVar
|
|
7981
|
+
};
|
|
7982
|
+
}
|
|
7612
7983
|
function deserializeField(ref, accessor, isRequired, walrusVar = "_v") {
|
|
7613
7984
|
if (isDateTimeType(ref)) {
|
|
7614
7985
|
if (isRequired) return `_parse_datetime(${accessor})`;
|
|
@@ -7639,17 +8010,6 @@ function deserializeField(ref, accessor, isRequired, walrusVar = "_v") {
|
|
|
7639
8010
|
case "union": {
|
|
7640
8011
|
const modelVariants = (ref.variants ?? []).filter((v) => v.kind === "model");
|
|
7641
8012
|
const uniqueModels = [...new Set(modelVariants.map((v) => v.name))];
|
|
7642
|
-
if (ref.discriminator && ref.discriminator.mapping) {
|
|
7643
|
-
const entries = Object.entries(ref.discriminator.mapping);
|
|
7644
|
-
if (entries.length > 0) {
|
|
7645
|
-
const dispatchMap = entries.map(([value, modelName]) => `"${value}": ${className$5(modelName)}`).join(", ");
|
|
7646
|
-
const dataExpr = isRequired ? accessor : walrusVar;
|
|
7647
|
-
const dataCast = `cast(Dict[str, Any], ${dataExpr})`;
|
|
7648
|
-
const branch = `(_disc.from_dict(${dataCast}) if (_disc := ${`{${dispatchMap}}.get(cast(str, ${dataCast}.get("${ref.discriminator.property}")))`}) is not None else ${dataExpr})`;
|
|
7649
|
-
if (isRequired) return branch;
|
|
7650
|
-
return `(${branch}) if (${walrusVar} := ${accessor}) is not None else None`;
|
|
7651
|
-
}
|
|
7652
|
-
}
|
|
7653
8013
|
if (uniqueModels.length === 1) return deserializeField({
|
|
7654
8014
|
kind: "model",
|
|
7655
8015
|
name: uniqueModels[0]
|
|
@@ -7671,61 +8031,12 @@ function serializeField(ref, accessor) {
|
|
|
7671
8031
|
case "union": {
|
|
7672
8032
|
const modelVariants = (ref.variants ?? []).filter((v) => v.kind === "model");
|
|
7673
8033
|
if ([...new Set(modelVariants.map((v) => v.name))].length === 1) return `${accessor}.to_dict()`;
|
|
7674
|
-
if (ref.discriminator && ref.discriminator.mapping && modelVariants.length > 0) return `${accessor}.to_dict()
|
|
8034
|
+
if (ref.discriminator && ref.discriminator.mapping && modelVariants.length > 0) return `${accessor}.to_dict()`;
|
|
7675
8035
|
return accessor;
|
|
7676
8036
|
}
|
|
7677
8037
|
default: return accessor;
|
|
7678
8038
|
}
|
|
7679
8039
|
}
|
|
7680
|
-
/**
|
|
7681
|
-
* Build recursive structural hashes for all models.
|
|
7682
|
-
*
|
|
7683
|
-
* Model references are resolved to their own structural hash (bottom-up) and
|
|
7684
|
-
* enum references are resolved to their value-set hash. This means
|
|
7685
|
-
* structurally-identical model *trees* — like the dozens of per-event Context /
|
|
7686
|
-
* ContextActor / ContextGoogleAnalyticsSession sub-models in the spec — get
|
|
7687
|
-
* the same hash even though their IR names differ.
|
|
7688
|
-
*/
|
|
7689
|
-
function buildRecursiveHashMap$1(models, enums) {
|
|
7690
|
-
const modelByName = new Map(models.map((m) => [m.name, m]));
|
|
7691
|
-
const hashCache = /* @__PURE__ */ new Map();
|
|
7692
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
7693
|
-
const enumVH = /* @__PURE__ */ new Map();
|
|
7694
|
-
for (const e of enums) enumVH.set(e.name, [...e.values].map((v) => String(v.value)).sort().join("|"));
|
|
7695
|
-
function modelHash(name) {
|
|
7696
|
-
const cached = hashCache.get(name);
|
|
7697
|
-
if (cached != null) return cached;
|
|
7698
|
-
if (visiting.has(name)) return `m:${name}`;
|
|
7699
|
-
visiting.add(name);
|
|
7700
|
-
const model = modelByName.get(name);
|
|
7701
|
-
if (!model) {
|
|
7702
|
-
visiting.delete(name);
|
|
7703
|
-
return `m:${name}`;
|
|
7704
|
-
}
|
|
7705
|
-
const hash = [...model.fields].sort((a, b) => a.name.localeCompare(b.name)).map((f) => `${f.name}:${deepTypeHash(f.type)}:${f.required}`).join("|");
|
|
7706
|
-
visiting.delete(name);
|
|
7707
|
-
hashCache.set(name, hash);
|
|
7708
|
-
return hash;
|
|
7709
|
-
}
|
|
7710
|
-
function deepTypeHash(ref) {
|
|
7711
|
-
switch (ref.kind) {
|
|
7712
|
-
case "primitive": return `p:${ref.type}${ref.format ? `:${ref.format}` : ""}`;
|
|
7713
|
-
case "model": return `m:{${modelHash(ref.name)}}`;
|
|
7714
|
-
case "enum": {
|
|
7715
|
-
const vh = enumVH.get(ref.name);
|
|
7716
|
-
return vh != null ? `e:{${vh}}` : `e:${ref.name}`;
|
|
7717
|
-
}
|
|
7718
|
-
case "array": return `a:${deepTypeHash(ref.items)}`;
|
|
7719
|
-
case "nullable": return `n:${deepTypeHash(ref.inner)}`;
|
|
7720
|
-
case "union": return `u:${(ref.variants ?? []).map((v) => deepTypeHash(v)).sort().join(",")}`;
|
|
7721
|
-
case "map": return `d:${deepTypeHash(ref.valueType)}`;
|
|
7722
|
-
case "literal": return `l:${String(ref.value)}`;
|
|
7723
|
-
default: return "unknown";
|
|
7724
|
-
}
|
|
7725
|
-
}
|
|
7726
|
-
for (const model of models) modelHash(model.name);
|
|
7727
|
-
return hashCache;
|
|
7728
|
-
}
|
|
7729
8040
|
//#endregion
|
|
7730
8041
|
//#region src/python/path-expression.ts
|
|
7731
8042
|
/**
|
|
@@ -8031,7 +8342,9 @@ function emitMethodSignature(lines, op, plan, method, isAsync, specEnumNames, mo
|
|
|
8031
8342
|
lines.push(" after: Optional[str] = None,");
|
|
8032
8343
|
const orderParam = op.queryParams.find((p) => p.name === "order");
|
|
8033
8344
|
const orderType = orderParam && orderParam.type.kind === "enum" ? mapTypeRefUnquoted(orderParam.type, specEnumNames, true) : "str";
|
|
8034
|
-
|
|
8345
|
+
const orderDefaultRaw = orderParam?.default;
|
|
8346
|
+
const orderDefault = typeof orderDefaultRaw === "string" || typeof orderDefaultRaw === "number" || typeof orderDefaultRaw === "boolean" ? pythonLiteral(orderDefaultRaw) : "None";
|
|
8347
|
+
lines.push(` order: Optional[${orderType}] = ${orderDefault},`);
|
|
8035
8348
|
for (const param of op.queryParams) {
|
|
8036
8349
|
if ([
|
|
8037
8350
|
"limit",
|
|
@@ -8578,7 +8891,8 @@ function generateResources$5(services, ctx) {
|
|
|
8578
8891
|
if (enumImports.size > 0) lines.push(`from ${importPrefix}_types import RequestOptions, enum_value`);
|
|
8579
8892
|
else lines.push(`from ${importPrefix}_types import RequestOptions`);
|
|
8580
8893
|
const actualModelImports = [...modelImports];
|
|
8581
|
-
const
|
|
8894
|
+
const placement = computeSchemaPlacement(ctx.spec, ctx);
|
|
8895
|
+
const modelToServiceMap = new Map(placement.modelToService);
|
|
8582
8896
|
for (const model of ctx.spec.models) if (model.discriminator) {
|
|
8583
8897
|
const svc = modelToServiceMap.get(model.name);
|
|
8584
8898
|
if (svc) modelToServiceMap.set(model.name + "Variant", svc);
|
|
@@ -8603,19 +8917,7 @@ function generateResources$5(services, ctx) {
|
|
|
8603
8917
|
const unique = names.filter((n) => !localSet.has(n));
|
|
8604
8918
|
for (const n of unique) lines.push(`from ${ctx.namespace}.${dirToModule(csDir)}.models.${fileName$2(n)} import ${className$5(n)}`);
|
|
8605
8919
|
}
|
|
8606
|
-
const enumToServiceMap =
|
|
8607
|
-
for (const e of ctx.spec.enums) for (const svc of ctx.spec.services) for (const op of svc.operations) {
|
|
8608
|
-
const refs = /* @__PURE__ */ new Set();
|
|
8609
|
-
const allTypeRefs = [
|
|
8610
|
-
op.response,
|
|
8611
|
-
...op.requestBody ? [op.requestBody] : [],
|
|
8612
|
-
...op.pathParams.map((p) => p.type),
|
|
8613
|
-
...op.queryParams.map((p) => p.type),
|
|
8614
|
-
...op.headerParams.map((p) => p.type)
|
|
8615
|
-
];
|
|
8616
|
-
for (const typeRef of allTypeRefs) for (const ref of collectEnumRefs(typeRef)) refs.add(ref);
|
|
8617
|
-
if (refs.has(e.name) && !enumToServiceMap.has(e.name)) enumToServiceMap.set(e.name, svc.name);
|
|
8618
|
-
}
|
|
8920
|
+
const enumToServiceMap = placement.enumToService;
|
|
8619
8921
|
const localEnums = [];
|
|
8620
8922
|
const crossServiceEnums = /* @__PURE__ */ new Map();
|
|
8621
8923
|
for (const name of [...enumImports].sort()) {
|
|
@@ -9387,13 +9689,14 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
|
|
|
9387
9689
|
if (!model) return true;
|
|
9388
9690
|
return !isListWrapperModel(model);
|
|
9389
9691
|
});
|
|
9390
|
-
const
|
|
9692
|
+
const placement = computeSchemaPlacement(spec, ctx);
|
|
9693
|
+
const modelToServiceMap = placement.modelToService;
|
|
9694
|
+
const enumToServiceMap = placement.enumToService;
|
|
9391
9695
|
const mountDirMap = buildMountDirMap$1(ctx);
|
|
9392
9696
|
const resolveModelDir = (modelName) => {
|
|
9393
9697
|
const svc = modelToServiceMap.get(modelName);
|
|
9394
9698
|
return svc ? mountDirMap.get(svc) ?? "common" : "common";
|
|
9395
9699
|
};
|
|
9396
|
-
const enumToServiceMap = assignEnumsToServices(spec.enums, spec.services);
|
|
9397
9700
|
const resolveEnumDir = (enumName) => {
|
|
9398
9701
|
const svc = enumToServiceMap.get(enumName);
|
|
9399
9702
|
return svc ? mountDirMap.get(svc) ?? "common" : "common";
|
|
@@ -10237,7 +10540,7 @@ function generateModelRoundTripTests(spec, ctx) {
|
|
|
10237
10540
|
for (const name of responseModelNames) requestOnlyModelNames.delete(name);
|
|
10238
10541
|
const models = spec.models.filter((m) => !isListWrapperModel(m) && !isListMetadataModel(m) && !requestOnlyModelNames.has(m.name));
|
|
10239
10542
|
if (models.length === 0) return null;
|
|
10240
|
-
const modelToService =
|
|
10543
|
+
const modelToService = computeSchemaPlacement(spec, ctx).originalModelToService;
|
|
10241
10544
|
const roundTripDirMap = buildMountDirMap$1(ctx);
|
|
10242
10545
|
const resolveDir = (irService) => irService ? roundTripDirMap.get(irService) ?? "common" : "common";
|
|
10243
10546
|
const lines = [];
|
|
@@ -10798,7 +11101,8 @@ function generateFromArrayValue(ref, accessor) {
|
|
|
10798
11101
|
const entries = Object.entries(ref.discriminator.mapping);
|
|
10799
11102
|
if (entries.length > 0) {
|
|
10800
11103
|
const arms = entries.map(([value, modelName]) => `'${value}' => ${className$4(modelName)}::fromArray(${accessor})`).join(", ");
|
|
10801
|
-
|
|
11104
|
+
const discProp = ref.discriminator.property;
|
|
11105
|
+
return `match (${accessor}['${discProp}'] ?? null) { ${arms}, ${`default => throw new \\UnexpectedValueException(sprintf('Unknown ${discProp}: %s', json_encode(${accessor}['${discProp}'] ?? null)))`} }`;
|
|
10802
11106
|
}
|
|
10803
11107
|
}
|
|
10804
11108
|
const resolved = resolveDegenerateUnion(ref);
|
|
@@ -10854,6 +11158,11 @@ function generateToArrayValue(ref, accessor, nullable = false) {
|
|
|
10854
11158
|
case "union": {
|
|
10855
11159
|
const resolved = resolveDegenerateUnion(ref);
|
|
10856
11160
|
if (resolved) return generateToArrayValue(resolved, accessor, nullable);
|
|
11161
|
+
if (ref.variants.every((v) => v.kind === "model")) return `${accessor}${ns}->toArray()`;
|
|
11162
|
+
if (ref.variants.some((v) => v.kind === "model" || v.kind === "enum")) {
|
|
11163
|
+
const summary = ref.variants.map((v) => v.kind === "model" ? `model:${v.name}` : v.kind === "enum" ? `enum:${v.name}` : v.kind).join(" | ");
|
|
11164
|
+
throw new Error(`[php emitter] Cannot generate toArray for heterogeneous union: ${summary}. Unions must be all-model or all-scalar; mixed and all-enum unions are not yet supported.`);
|
|
11165
|
+
}
|
|
10857
11166
|
return accessor;
|
|
10858
11167
|
}
|
|
10859
11168
|
case "literal": return accessor;
|
|
@@ -11235,8 +11544,8 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
|
|
|
11235
11544
|
const phpName = fieldName$3(q.name);
|
|
11236
11545
|
if (seenDocParams.has(phpName)) continue;
|
|
11237
11546
|
seenDocParams.add(phpName);
|
|
11238
|
-
const
|
|
11239
|
-
const nullSuffix = !q.required && !
|
|
11547
|
+
const hasEnumDefault = q.default != null && q.type.kind === "enum";
|
|
11548
|
+
const nullSuffix = !q.required && !hasEnumDefault && !docType.endsWith("|null") ? "|null" : "";
|
|
11240
11549
|
const prefix = q.deprecated ? "(deprecated) " : "";
|
|
11241
11550
|
let desc = q.description ? ` ${prefix}${q.description}` : q.deprecated ? " (deprecated)" : "";
|
|
11242
11551
|
if (q.default != null) desc += ` Defaults to ${JSON.stringify(q.default)}.`;
|
|
@@ -11458,15 +11767,11 @@ function buildMethodParams(op, plan, modelMap, ctx, hiddenParams) {
|
|
|
11458
11767
|
if (usedNames.has(phpName)) continue;
|
|
11459
11768
|
usedNames.add(phpName);
|
|
11460
11769
|
if (q.required) required.push(`${phpType} $${phpName}`);
|
|
11461
|
-
else if (q.
|
|
11770
|
+
else if (q.default != null && q.type.kind === "enum") {
|
|
11462
11771
|
const enumType = mapTypeRef$4(q.type, { qualified: true });
|
|
11463
|
-
const caseName = toPascalCase(
|
|
11772
|
+
const caseName = toPascalCase(String(q.default));
|
|
11464
11773
|
optional.push(`${enumType} $${phpName} = ${enumType}::${caseName}`);
|
|
11465
11774
|
} else {
|
|
11466
|
-
const nullableType = phpType.startsWith("?") ? phpType : `?${phpType}`;
|
|
11467
|
-
optional.push(`${nullableType} $${phpName} = 'desc'`);
|
|
11468
|
-
}
|
|
11469
|
-
else {
|
|
11470
11775
|
const nullableType = phpType.startsWith("?") ? phpType : `?${phpType}`;
|
|
11471
11776
|
optional.push(`${nullableType} $${phpName} = null`);
|
|
11472
11777
|
}
|
|
@@ -11514,8 +11819,8 @@ function buildQueryArray(op, hiddenParams) {
|
|
|
11514
11819
|
return op.queryParams.filter((q) => !hidden.has(q.name) && !groupedParams.has(q.name)).map((q) => {
|
|
11515
11820
|
const phpName = fieldName$3(q.name);
|
|
11516
11821
|
if (isEnumType(q.type)) {
|
|
11517
|
-
const
|
|
11518
|
-
const nullsafe = q.required ||
|
|
11822
|
+
const hasEnumDefault = q.default != null && q.type.kind === "enum";
|
|
11823
|
+
const nullsafe = q.required || hasEnumDefault ? "" : "?";
|
|
11519
11824
|
return `'${q.name}' => $${phpName}${nullsafe}->value,`;
|
|
11520
11825
|
}
|
|
11521
11826
|
return `'${q.name}' => $${phpName},`;
|
|
@@ -12611,6 +12916,8 @@ function generateModels$3(models, ctx) {
|
|
|
12611
12916
|
lines.push("}");
|
|
12612
12917
|
lines.push("");
|
|
12613
12918
|
}
|
|
12919
|
+
const orderEnumType = detectSharedOrderEnum(ctx.spec.services);
|
|
12920
|
+
const orderGoType = orderEnumType ? `*${className$3(orderEnumType)}` : "*string";
|
|
12614
12921
|
lines.push("// PaginationParams contains common pagination parameters for list operations.");
|
|
12615
12922
|
lines.push("type PaginationParams struct {");
|
|
12616
12923
|
lines.push(" // Before is a cursor for reverse pagination.");
|
|
@@ -12619,8 +12926,8 @@ function generateModels$3(models, ctx) {
|
|
|
12619
12926
|
lines.push(" After *string `url:\"after,omitempty\" json:\"-\"`");
|
|
12620
12927
|
lines.push(" // Limit is the maximum number of items to return per page.");
|
|
12621
12928
|
lines.push(" Limit *int `url:\"limit,omitempty\" json:\"-\"`");
|
|
12622
|
-
lines.push(" // Order is the sort order for results
|
|
12623
|
-
lines.push(
|
|
12929
|
+
lines.push(" // Order is the sort order for results.");
|
|
12930
|
+
lines.push(`\tOrder ${orderGoType} \`url:"order,omitempty" json:"-"\``);
|
|
12624
12931
|
lines.push("}");
|
|
12625
12932
|
lines.push("");
|
|
12626
12933
|
files.push({
|
|
@@ -12700,6 +13007,38 @@ function lowerFirst$1(s) {
|
|
|
12700
13007
|
return lowerFirstForDoc(s);
|
|
12701
13008
|
}
|
|
12702
13009
|
/**
|
|
13010
|
+
* If every paginated list operation's `order` query parameter $refs the same
|
|
13011
|
+
* top-level enum, return that enum's IR name. Otherwise return null. When
|
|
13012
|
+
* the spec is consistent this lifts `PaginationParams.Order` from `*string`
|
|
13013
|
+
* to `*PaginationOrder` (or whatever the spec calls it), giving callers
|
|
13014
|
+
* compile-time validation.
|
|
13015
|
+
*
|
|
13016
|
+
* We require strict consistency: if any operation uses a primitive string for
|
|
13017
|
+
* `order`, or two operations reference different enums, we conservatively
|
|
13018
|
+
* stay on `*string` so the shared struct doesn't lie about its accepted
|
|
13019
|
+
* values.
|
|
13020
|
+
*/
|
|
13021
|
+
function detectSharedOrderEnum(services) {
|
|
13022
|
+
let candidate = null;
|
|
13023
|
+
let sawAny = false;
|
|
13024
|
+
for (const service of services) for (const op of service.operations) {
|
|
13025
|
+
if (!op.pagination) continue;
|
|
13026
|
+
const orderParam = op.queryParams.find((p) => p.name === "order");
|
|
13027
|
+
if (!orderParam) continue;
|
|
13028
|
+
sawAny = true;
|
|
13029
|
+
const enumName = unwrapEnumName(orderParam.type);
|
|
13030
|
+
if (!enumName) return null;
|
|
13031
|
+
if (candidate === null) candidate = enumName;
|
|
13032
|
+
else if (candidate !== enumName) return null;
|
|
13033
|
+
}
|
|
13034
|
+
return sawAny ? candidate : null;
|
|
13035
|
+
}
|
|
13036
|
+
function unwrapEnumName(ref) {
|
|
13037
|
+
if (ref.kind === "enum") return ref.name;
|
|
13038
|
+
if (ref.kind === "nullable") return unwrapEnumName(ref.inner);
|
|
13039
|
+
return null;
|
|
13040
|
+
}
|
|
13041
|
+
/**
|
|
12703
13042
|
* Extract a deprecation reason from a field description.
|
|
12704
13043
|
* Looks for patterns like "Use X instead", "Replaced by Y", etc.
|
|
12705
13044
|
* Falls back to a generic message if no migration guidance is found.
|
|
@@ -15558,15 +15897,22 @@ function generateEnums$2(enums, ctx) {
|
|
|
15558
15897
|
lines.push(" [STJS.JsonConverter(typeof(WorkOSStringEnumConverterFactory))]");
|
|
15559
15898
|
lines.push(` public enum ${typeName}`);
|
|
15560
15899
|
lines.push(" {");
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
lines.push("");
|
|
15900
|
+
const defaultMatch = enumDef.default !== void 0 ? uniqueValues.find((v) => v.value === enumDef.default) : void 0;
|
|
15901
|
+
const orderedValues = defaultMatch ? [defaultMatch, ...uniqueValues.filter((v) => v !== defaultMatch)] : uniqueValues;
|
|
15564
15902
|
const usedNames = /* @__PURE__ */ new Set();
|
|
15565
|
-
usedNames.add("Unknown");
|
|
15566
15903
|
const usedWireValues = /* @__PURE__ */ new Set();
|
|
15567
|
-
|
|
15568
|
-
|
|
15569
|
-
|
|
15904
|
+
if (defaultMatch) {
|
|
15905
|
+
usedNames.add("Unknown");
|
|
15906
|
+
usedWireValues.add("unknown");
|
|
15907
|
+
} else {
|
|
15908
|
+
lines.push(` [EnumMember(Value = "unknown")]`);
|
|
15909
|
+
lines.push(` Unknown,`);
|
|
15910
|
+
lines.push("");
|
|
15911
|
+
usedNames.add("Unknown");
|
|
15912
|
+
usedWireValues.add("unknown");
|
|
15913
|
+
}
|
|
15914
|
+
for (let i = 0; i < orderedValues.length; i++) {
|
|
15915
|
+
const v = orderedValues[i];
|
|
15570
15916
|
if (usedWireValues.has(String(v.value))) continue;
|
|
15571
15917
|
usedWireValues.add(String(v.value));
|
|
15572
15918
|
let memberName = className$2(String(v.value));
|
|
@@ -15582,8 +15928,13 @@ function generateEnums$2(enums, ctx) {
|
|
|
15582
15928
|
lines.push(` [System.Obsolete("${msg}")]`);
|
|
15583
15929
|
}
|
|
15584
15930
|
lines.push(` [EnumMember(Value = "${v.value}")]`);
|
|
15585
|
-
const
|
|
15586
|
-
lines.push(` ${memberName}${
|
|
15931
|
+
const ordinal = defaultMatch ? ` = ${i}` : "";
|
|
15932
|
+
lines.push(` ${memberName}${ordinal},`);
|
|
15933
|
+
}
|
|
15934
|
+
if (defaultMatch) {
|
|
15935
|
+
lines.push("");
|
|
15936
|
+
lines.push(` [EnumMember(Value = "unknown")]`);
|
|
15937
|
+
lines.push(` Unknown = 99,`);
|
|
15587
15938
|
}
|
|
15588
15939
|
lines.push(" }");
|
|
15589
15940
|
lines.push("}");
|
|
@@ -21415,7 +21766,7 @@ function emitMethod(args) {
|
|
|
21415
21766
|
const n = safeParamName(q.name);
|
|
21416
21767
|
if (seenParamNames.has(n)) continue;
|
|
21417
21768
|
seenParamNames.add(n);
|
|
21418
|
-
const defaultVal = q.
|
|
21769
|
+
const defaultVal = q.default != null ? rubyDefaultLiteral(q.default) : "nil";
|
|
21419
21770
|
sigParts.push(`${n}: ${defaultVal}`);
|
|
21420
21771
|
}
|
|
21421
21772
|
for (const group of op.parameterGroups ?? []) {
|
|
@@ -21795,6 +22146,14 @@ function buildYardDoc(op, pathParams, queryParams, bodyFields, hiddenParams, bod
|
|
|
21795
22146
|
function rubyStringLit(s) {
|
|
21796
22147
|
return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
21797
22148
|
}
|
|
22149
|
+
/** Render an arbitrary spec-default value as a Ruby literal for a method-arg default. */
|
|
22150
|
+
function rubyDefaultLiteral(value) {
|
|
22151
|
+
if (typeof value === "string") return rubyStringLit(value);
|
|
22152
|
+
if (typeof value === "number") return Number.isFinite(value) ? String(value) : "nil";
|
|
22153
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
22154
|
+
if (value === null) return "nil";
|
|
22155
|
+
return "nil";
|
|
22156
|
+
}
|
|
21798
22157
|
/**
|
|
21799
22158
|
* Build a Ruby double-quoted string expression for the `else raise ArgumentError`
|
|
21800
22159
|
* arm of a parameter-group dispatcher. Lists the expected variant classes and
|
|
@@ -22800,4 +23159,4 @@ const workosEmittersPlugin = {
|
|
|
22800
23159
|
//#endregion
|
|
22801
23160
|
export { nodeEmitter as _, rustExtractor as a, pythonExtractor as c, rubyEmitter as d, kotlinEmitter as f, pythonEmitter as g, phpEmitter as h, kotlinExtractor as i, rubyExtractor as l, goEmitter as m, elixirExtractor as n, goExtractor as o, dotnetEmitter as p, dotnetExtractor as r, phpExtractor as s, workosEmittersPlugin as t, nodeExtractor as u };
|
|
22802
23161
|
|
|
22803
|
-
//# sourceMappingURL=plugin-
|
|
23162
|
+
//# sourceMappingURL=plugin-DW3cnedr.mjs.map
|