@workos/oagen-emitters 0.18.3 → 0.18.4
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 +9 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{plugin-1ckLMpgo.mjs → plugin-Cciic50q.mjs} +443 -99
- package/dist/plugin-Cciic50q.mjs.map +1 -0
- package/dist/plugin.mjs +1 -1
- package/docs/sdk-architecture/rust.md +2 -2
- package/package.json +3 -3
- package/src/dotnet/fixtures.ts +5 -2
- package/src/dotnet/index.ts +2 -1
- package/src/dotnet/models.ts +30 -5
- package/src/dotnet/naming.ts +10 -0
- package/src/dotnet/tests.ts +5 -1
- package/src/go/fixtures.ts +4 -2
- package/src/go/index.ts +4 -0
- package/src/go/models.ts +4 -2
- package/src/go/naming.ts +10 -0
- package/src/go/resources.ts +19 -6
- package/src/kotlin/index.ts +2 -1
- package/src/kotlin/models.ts +5 -2
- package/src/kotlin/naming.ts +11 -0
- package/src/kotlin/tests.ts +5 -1
- package/src/node/field-plan.ts +3 -3
- package/src/node/index.ts +2 -1
- package/src/node/models.ts +40 -1
- package/src/node/naming.ts +10 -0
- package/src/node/options.ts +45 -1
- package/src/node/resources.ts +55 -17
- package/src/node/tests.ts +296 -30
- package/src/php/index.ts +2 -1
- package/src/php/models.ts +11 -5
- package/src/php/naming.ts +10 -0
- package/src/php/tests.ts +11 -2
- package/src/python/fixtures.ts +4 -3
- package/src/python/index.ts +2 -1
- package/src/python/models.ts +12 -6
- package/src/python/naming.ts +10 -0
- package/src/python/tests.ts +11 -6
- package/src/ruby/index.ts +2 -1
- package/src/ruby/models.ts +10 -7
- package/src/ruby/naming.ts +10 -0
- package/src/ruby/rbi.ts +3 -1
- package/src/ruby/tests.ts +4 -1
- package/src/rust/index.ts +2 -1
- package/src/rust/models.ts +87 -15
- package/src/rust/naming.ts +10 -0
- package/src/rust/resources.ts +6 -2
- package/src/shared/file-header.ts +13 -0
- package/test/rust/models.test.ts +49 -0
- package/dist/plugin-1ckLMpgo.mjs.map +0 -1
|
@@ -3637,6 +3637,29 @@ function isHandOwnedType(ctx, name) {
|
|
|
3637
3637
|
if (!configured || configured.length === 0) return false;
|
|
3638
3638
|
return configured.includes(name);
|
|
3639
3639
|
}
|
|
3640
|
+
/**
|
|
3641
|
+
* Resolve the Node operation override for an operation, keyed by "METHOD /path".
|
|
3642
|
+
*/
|
|
3643
|
+
function operationOverrideFor$1(ctx, op) {
|
|
3644
|
+
return nodeOptions(ctx).operationOverrides?.[`${op.httpMethod.toUpperCase()} ${op.path}`];
|
|
3645
|
+
}
|
|
3646
|
+
/**
|
|
3647
|
+
* `planOperation` plus the Node `responseModel` override. When an operation
|
|
3648
|
+
* override supplies `responseModel`, the resolved response model name is
|
|
3649
|
+
* replaced so the resource and its generated test reference the desired wire
|
|
3650
|
+
* type and deserializer. Use this everywhere the Node emitter would otherwise
|
|
3651
|
+
* call `planOperation(op)` directly so resource and test stay in lockstep.
|
|
3652
|
+
*/
|
|
3653
|
+
function planOperationFor(op, ctx) {
|
|
3654
|
+
const plan = planOperation(op);
|
|
3655
|
+
const responseModel = operationOverrideFor$1(ctx, op)?.responseModel;
|
|
3656
|
+
if (responseModel && responseModel !== plan.responseModelName) return {
|
|
3657
|
+
...plan,
|
|
3658
|
+
responseModelName: responseModel,
|
|
3659
|
+
isModelResponse: true
|
|
3660
|
+
};
|
|
3661
|
+
return plan;
|
|
3662
|
+
}
|
|
3640
3663
|
//#endregion
|
|
3641
3664
|
//#region src/node/live-surface.ts
|
|
3642
3665
|
const SRC_DIR = "src";
|
|
@@ -4913,7 +4936,7 @@ function serializerHasBaselineIncompatibility(model, baselineResponse, baselineD
|
|
|
4913
4936
|
const irDomainFields = /* @__PURE__ */ new Set();
|
|
4914
4937
|
for (const field of model.fields) {
|
|
4915
4938
|
irWireFields.add(wireFieldName(field.name));
|
|
4916
|
-
irDomainFields.add(fieldName$6(field.name));
|
|
4939
|
+
irDomainFields.add(fieldName$6(field.domainName ?? field.name));
|
|
4917
4940
|
}
|
|
4918
4941
|
for (const [wireField2, fieldDef] of Object.entries(baselineResponse.fields)) {
|
|
4919
4942
|
if (fieldDef.optional) continue;
|
|
@@ -4957,7 +4980,7 @@ function serializerHasBaselineIncompatibility(model, baselineResponse, baselineD
|
|
|
4957
4980
|
return false;
|
|
4958
4981
|
}
|
|
4959
4982
|
function planDeserializeField(field, baselineDomain, baselineResponse, skipFormatFields, ctx) {
|
|
4960
|
-
const domain = fieldName$6(field.name);
|
|
4983
|
+
const domain = fieldName$6(field.domainName ?? field.name);
|
|
4961
4984
|
const wireAccess = `response.${wireFieldName(field.name)}`;
|
|
4962
4985
|
const skip = skipFormatFields.has(field.name);
|
|
4963
4986
|
const baselineDomainField = baselineDomain?.fields?.[domain];
|
|
@@ -4993,7 +5016,7 @@ function planDeserializeGuard(field, expr, wireAccess, effectivelyOptional, isNe
|
|
|
4993
5016
|
}
|
|
4994
5017
|
function planSerializeField(field, baselineDomain, baselineResponse, skipFormatFields, ctx) {
|
|
4995
5018
|
const wire = wireFieldName(field.name);
|
|
4996
|
-
const domain = fieldName$6(field.name);
|
|
5019
|
+
const domain = fieldName$6(field.domainName ?? field.name);
|
|
4997
5020
|
const domainAccess = `model.${domain}`;
|
|
4998
5021
|
const skip = skipFormatFields.has(field.name);
|
|
4999
5022
|
const baselineWireField = baselineResponse?.fields?.[wire];
|
|
@@ -5533,9 +5556,6 @@ function existingInterfaceBarrelExports(ctx, serviceDir, stem) {
|
|
|
5533
5556
|
const escapedStem = stem.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5534
5557
|
return new RegExp(`export\\s+(?:type\\s+)?(?:\\*|\\{[^}]+\\})\\s+from\\s+['"]\\./${escapedStem}['"]`).test(content);
|
|
5535
5558
|
}
|
|
5536
|
-
function operationOverrideFor$1(ctx, op) {
|
|
5537
|
-
return nodeOptions(ctx).operationOverrides?.[`${op.httpMethod.toUpperCase()} ${op.path}`];
|
|
5538
|
-
}
|
|
5539
5559
|
function baselineMethodFor$1(service, method, ctx) {
|
|
5540
5560
|
const serviceClass = resolveResourceClassName$3(service, ctx);
|
|
5541
5561
|
return ctx.apiSurface?.classes?.[serviceClass]?.methods?.[method]?.[0];
|
|
@@ -5561,10 +5581,11 @@ function optionsObjectParam$1(method) {
|
|
|
5561
5581
|
const [param] = method.params;
|
|
5562
5582
|
if (param.name !== "options") return void 0;
|
|
5563
5583
|
if (param.passingStyle && param.passingStyle !== "options_object") return void 0;
|
|
5564
|
-
|
|
5584
|
+
const type = param.type?.replace(/(?:\s*\|\s*(?:undefined|null))+\s*$/, "").trim();
|
|
5585
|
+
if (!type || /^(Record|object|any|unknown)\b/.test(type)) return void 0;
|
|
5565
5586
|
return {
|
|
5566
5587
|
name: "options",
|
|
5567
|
-
type
|
|
5588
|
+
type,
|
|
5568
5589
|
optional: param.optional === true,
|
|
5569
5590
|
generated: false
|
|
5570
5591
|
};
|
|
@@ -5765,14 +5786,14 @@ function generateOptionsInterfaces(service, ctx, specEnumNames) {
|
|
|
5765
5786
|
const resolvedLookup = buildResolvedLookup(ctx);
|
|
5766
5787
|
const plans = service.operations.map((op) => ({
|
|
5767
5788
|
op,
|
|
5768
|
-
plan:
|
|
5789
|
+
plan: planOperationFor(op, ctx),
|
|
5769
5790
|
method: resolveMethodName$6(op, service, ctx)
|
|
5770
5791
|
}));
|
|
5771
5792
|
for (const { op, plan, method } of plans) {
|
|
5772
5793
|
const resolvedOp = lookupResolved(op, resolvedLookup);
|
|
5773
5794
|
const optionInfo = optionsObjectInfo(service, method, op, plan, ctx, baselineMethodFor$1(service, method, ctx), resolvedOp);
|
|
5774
5795
|
if (!optionInfo?.generated) continue;
|
|
5775
|
-
if (baselineTypeSourceFile(ctx, optionInfo.type)) continue;
|
|
5796
|
+
if (op.pathParams.length === 0 && baselineTypeSourceFile(ctx, optionInfo.type)) continue;
|
|
5776
5797
|
const optionsName = optionInfo.type;
|
|
5777
5798
|
const optionFileStem = `${fileName$3(optionsName)}.interface`;
|
|
5778
5799
|
const filePath = `src/${serviceDir}/interfaces/${optionFileStem}.ts`;
|
|
@@ -5815,7 +5836,8 @@ function generateOptionsInterfaces(service, ctx, specEnumNames) {
|
|
|
5815
5836
|
}
|
|
5816
5837
|
headerParts.push(` ${name}${opt}: ${type};`);
|
|
5817
5838
|
};
|
|
5818
|
-
|
|
5839
|
+
const optionsPathFieldMap = operationOverrideFor$1(ctx, op)?.pathFieldMap;
|
|
5840
|
+
for (const param of op.pathParams) pushField(optionsPathFieldMap?.[fieldName$6(param.name)] ?? fieldName$6(param.name), true, mapParamType(param.type, specEnumNames), param.description, param.deprecated);
|
|
5819
5841
|
for (const param of visibleQueryParamsForOptions(op, plan, resolvedOp)) pushField(fieldName$6(param.name), param.required, mapParamType(param.type, specEnumNames), param.description, param.deprecated);
|
|
5820
5842
|
if (bodyInfo?.kind === "model") {
|
|
5821
5843
|
const bodyModel = ctx.spec.models.find((m) => m.name === bodyInfo.name);
|
|
@@ -5887,7 +5909,7 @@ function generateResourceClass(service, ctx) {
|
|
|
5887
5909
|
const resourcePath = `src/${serviceDir}/${fileName$3(resolvedName)}.ts`;
|
|
5888
5910
|
let plans = service.operations.map((op) => ({
|
|
5889
5911
|
op,
|
|
5890
|
-
plan:
|
|
5912
|
+
plan: planOperationFor(op, ctx),
|
|
5891
5913
|
method: resolveMethodName$6(op, service, ctx)
|
|
5892
5914
|
}));
|
|
5893
5915
|
deduplicateMethodNames(plans, ctx);
|
|
@@ -5996,6 +6018,7 @@ function generateResourceClass(service, ctx) {
|
|
|
5996
6018
|
const hasCustomEncoding = plans.some((p) => p.op.requestBodyEncoding && p.op.requestBodyEncoding !== "json" && p.plan.hasBody);
|
|
5997
6019
|
if (hasIdempotentPost || hasCustomEncoding) lines.push("import type { PostOptions } from '../common/interfaces/post-options.interface';");
|
|
5998
6020
|
const importedTypeNames = /* @__PURE__ */ new Set();
|
|
6021
|
+
if (needsPaginationOptionsImport) importedTypeNames.add("PaginationOptions");
|
|
5999
6022
|
for (const optionType of optionObjectTypes) {
|
|
6000
6023
|
if (isValidTypeIdentifier(optionType)) {
|
|
6001
6024
|
if (importedTypeNames.has(optionType)) continue;
|
|
@@ -6554,7 +6577,14 @@ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelM
|
|
|
6554
6577
|
lines.push(" }");
|
|
6555
6578
|
return true;
|
|
6556
6579
|
}
|
|
6557
|
-
|
|
6580
|
+
{
|
|
6581
|
+
lines.push(` async ${method}(${renderOptionsParam(optionParam)}): Promise<void> {`);
|
|
6582
|
+
renderOptionsObjectDestructure(lines, pathBindings);
|
|
6583
|
+
const emptyBodyArg = httpMethodNeedsBody(op.httpMethod) ? ", {}" : "";
|
|
6584
|
+
lines.push(` await this.workos.${op.httpMethod}(${pathStr}${emptyBodyArg}${queryOptionsArg});`);
|
|
6585
|
+
lines.push(" }");
|
|
6586
|
+
return true;
|
|
6587
|
+
}
|
|
6558
6588
|
}
|
|
6559
6589
|
function renderOptionsObjectDestructure(lines, pathBindings, restName) {
|
|
6560
6590
|
if (pathBindings.length > 0 && restName) lines.push(` const { ${pathBindings.join(", ")}, ...${restName} } = options;`);
|
|
@@ -6574,7 +6604,8 @@ function bodyArgExprWithParam(bodyExpr, paramName) {
|
|
|
6574
6604
|
return paramName === "payload" ? bodyExpr : bodyExpr.replace(/\bpayload\b/g, paramName);
|
|
6575
6605
|
}
|
|
6576
6606
|
function buildOptionsObjectPathBindings(op, optionType, ctx) {
|
|
6577
|
-
|
|
6607
|
+
const pathFieldMap = operationOverrideFor$1(ctx, op)?.pathFieldMap;
|
|
6608
|
+
return op.pathParams.map((param) => resolveOptionsObjectField$1(fieldName$6(param.name), optionType, ctx, pathFieldMap));
|
|
6578
6609
|
}
|
|
6579
6610
|
/**
|
|
6580
6611
|
* Map spec path-param names (e.g. `omId`) to the SDK field name exposed on
|
|
@@ -6585,14 +6616,17 @@ function buildOptionsObjectPathBindings(op, optionType, ctx) {
|
|
|
6585
6616
|
*/
|
|
6586
6617
|
function buildOptionsObjectPathParamMap(op, optionType, ctx) {
|
|
6587
6618
|
const map = /* @__PURE__ */ new Map();
|
|
6619
|
+
const pathFieldMap = operationOverrideFor$1(ctx, op)?.pathFieldMap;
|
|
6588
6620
|
for (const param of op.pathParams) {
|
|
6589
6621
|
const localName = fieldName$6(param.name);
|
|
6590
|
-
const sdkField = resolveOptionsObjectField$1(localName, optionType, ctx);
|
|
6622
|
+
const sdkField = resolveOptionsObjectField$1(localName, optionType, ctx, pathFieldMap);
|
|
6591
6623
|
if (sdkField !== localName) map.set(param.name, sdkField);
|
|
6592
6624
|
}
|
|
6593
6625
|
return map;
|
|
6594
6626
|
}
|
|
6595
|
-
function resolveOptionsObjectField$1(localName, optionType, ctx) {
|
|
6627
|
+
function resolveOptionsObjectField$1(localName, optionType, ctx, pathFieldMap) {
|
|
6628
|
+
const mapped = pathFieldMap?.[localName];
|
|
6629
|
+
if (mapped) return mapped;
|
|
6596
6630
|
const fields = ctx.apiSurface?.interfaces?.[optionType]?.fields;
|
|
6597
6631
|
if (!fields) return localName;
|
|
6598
6632
|
if (fields[localName]) return localName;
|
|
@@ -7548,7 +7582,7 @@ function generateModels$7(models, ctx, shared) {
|
|
|
7548
7582
|
else {
|
|
7549
7583
|
lines.push(`export interface ${domainName}${typeParams} {`);
|
|
7550
7584
|
for (const field of model.fields) {
|
|
7551
|
-
const domainFieldName = fieldName$6(field.name);
|
|
7585
|
+
const domainFieldName = fieldName$6(field.domainName ?? field.name);
|
|
7552
7586
|
if (seenDomainFields.has(domainFieldName)) continue;
|
|
7553
7587
|
seenDomainFields.add(domainFieldName);
|
|
7554
7588
|
if (field.description || field.deprecated || field.readOnly || field.writeOnly || field.default !== void 0) {
|
|
@@ -7565,7 +7599,10 @@ function generateModels$7(models, ctx, shared) {
|
|
|
7565
7599
|
const responseBaselineField = baselineResponse?.fields?.[domainWireField];
|
|
7566
7600
|
const domainResponseOptionalMismatch = baselineField && !baselineField.optional && responseBaselineField && responseBaselineField.optional;
|
|
7567
7601
|
const readonlyPrefix = field.readOnly ? "readonly " : "";
|
|
7568
|
-
if (
|
|
7602
|
+
if (genericDefaults.has(model.name) && baselineField && typeReferencesUnresolvable(baselineField.type, unresolvableNames)) {
|
|
7603
|
+
const opt = baselineField.optional ? "?" : "";
|
|
7604
|
+
lines.push(` ${readonlyPrefix}${domainFieldName}${opt}: ${substituteGenericParam(baselineField.type, unresolvableNames)};`);
|
|
7605
|
+
} else if (baselineField && !domainResponseOptionalMismatch && !hasDateTimeConversion(field.type) && baselineTypeResolvable(baselineField.type, importableNames) && baselineFieldCompatible(baselineField, field)) {
|
|
7569
7606
|
const opt = baselineField.optional ? "?" : "";
|
|
7570
7607
|
lines.push(` ${readonlyPrefix}${domainFieldName}${opt}: ${baselineField.type};`);
|
|
7571
7608
|
} else {
|
|
@@ -7588,7 +7625,10 @@ function generateModels$7(models, ctx, shared) {
|
|
|
7588
7625
|
if (seenWireFields.has(wireField)) continue;
|
|
7589
7626
|
seenWireFields.add(wireField);
|
|
7590
7627
|
const baselineField = baselineResponse?.fields?.[wireField];
|
|
7591
|
-
if (
|
|
7628
|
+
if (genericDefaults.has(model.name) && baselineField && typeReferencesUnresolvable(baselineField.type, unresolvableNames)) {
|
|
7629
|
+
const opt = baselineField.optional ? "?" : "";
|
|
7630
|
+
lines.push(` ${wireField}${opt}: ${substituteGenericParam(baselineField.type, unresolvableNames)};`);
|
|
7631
|
+
} else if (baselineField && baselineTypeResolvable(baselineField.type, importableNames) && baselineFieldCompatible(baselineField, field)) {
|
|
7592
7632
|
const opt = baselineField.optional ? "?" : "";
|
|
7593
7633
|
lines.push(` ${wireField}${opt}: ${baselineField.type};`);
|
|
7594
7634
|
} else {
|
|
@@ -8031,6 +8071,19 @@ function hasSpecificIRType(ref) {
|
|
|
8031
8071
|
default: return false;
|
|
8032
8072
|
}
|
|
8033
8073
|
}
|
|
8074
|
+
/** True when a baseline field type references one of the model's generic
|
|
8075
|
+
* param identifiers (collected as `unresolvableNames` — bare type names that
|
|
8076
|
+
* resolve to nothing importable, which for a generic model are its params). */
|
|
8077
|
+
function typeReferencesUnresolvable(type, unresolvable) {
|
|
8078
|
+
if (unresolvable.size === 0) return false;
|
|
8079
|
+
const names = type.match(/\b[A-Z][a-zA-Z0-9]*\b/g);
|
|
8080
|
+
return !!names && names.some((n) => unresolvable.has(n));
|
|
8081
|
+
}
|
|
8082
|
+
/** Rewrite a baseline field type so references to the model's (renamed)
|
|
8083
|
+
* generic param resolve to the emitted `GenericType` param. */
|
|
8084
|
+
function substituteGenericParam(type, unresolvable) {
|
|
8085
|
+
return type.replace(/\b[A-Z][a-zA-Z0-9]*\b/g, (name) => unresolvable.has(name) ? "GenericType" : name);
|
|
8086
|
+
}
|
|
8034
8087
|
function renderTypeParams(model, genericDefaults) {
|
|
8035
8088
|
if (!model.typeParams?.length) {
|
|
8036
8089
|
if (genericDefaults?.has(model.name)) return "<GenericType extends Record<string, unknown> = Record<string, unknown>>";
|
|
@@ -8557,10 +8610,11 @@ function optionsObjectParam(method) {
|
|
|
8557
8610
|
const [param] = method.params;
|
|
8558
8611
|
if (param.name !== "options") return void 0;
|
|
8559
8612
|
if (param.passingStyle && param.passingStyle !== "options_object") return void 0;
|
|
8560
|
-
|
|
8613
|
+
const type = param.type?.replace(/(?:\s*\|\s*(?:undefined|null))+\s*$/, "").trim();
|
|
8614
|
+
if (!type || /^(Record|object|any|unknown)\b/.test(type)) return void 0;
|
|
8561
8615
|
return {
|
|
8562
8616
|
name: param.name,
|
|
8563
|
-
type
|
|
8617
|
+
type
|
|
8564
8618
|
};
|
|
8565
8619
|
}
|
|
8566
8620
|
function configuredOptionsMethod(ctx, op) {
|
|
@@ -8625,6 +8679,7 @@ function generateTests$7(spec, ctx) {
|
|
|
8625
8679
|
};
|
|
8626
8680
|
const ops = isNodeOwnedService(ctx, mountName, resolveResourceClassName$3(mergedService, ctx)) ? operations : uncoveredOperations(mergedService, ctx);
|
|
8627
8681
|
if (ops.length === 0) continue;
|
|
8682
|
+
const ignoredMethodNames = ignoredResourceMethodNames(ctx, `src/${resolveResourceDir(mergedService, ctx)}/${fileName$3(resolveResourceClassName$3(mergedService, ctx))}.ts`);
|
|
8628
8683
|
const propName = mountAccessors.get(mountName) ?? servicePropertyName$4(mountName);
|
|
8629
8684
|
if (ctx.apiSurface && baselineWorkOSProps.size > 0 && !baselineWorkOSProps.has(propName)) continue;
|
|
8630
8685
|
if (resolveResourceClassName$3(mergedService, ctx) !== mountName) continue;
|
|
@@ -8632,23 +8687,31 @@ function generateTests$7(spec, ctx) {
|
|
|
8632
8687
|
...mergedService,
|
|
8633
8688
|
operations: ops
|
|
8634
8689
|
} : mergedService;
|
|
8635
|
-
files.push(generateServiceTest$3(testService, spec, ctx, modelMap, mountAccessors));
|
|
8690
|
+
files.push(generateServiceTest$3(testService, spec, ctx, modelMap, mountAccessors, ignoredMethodNames));
|
|
8636
8691
|
}
|
|
8637
8692
|
const serializerTests = generateSerializerTests(spec, ctx);
|
|
8638
8693
|
for (const f of serializerTests) files.push(f);
|
|
8639
8694
|
return files;
|
|
8640
8695
|
}
|
|
8641
|
-
function generateServiceTest$3(service, spec, ctx, modelMap, mountAccessors) {
|
|
8696
|
+
function generateServiceTest$3(service, spec, ctx, modelMap, mountAccessors, ignoredMethodNames = /* @__PURE__ */ new Set()) {
|
|
8642
8697
|
const resolvedName = resolveResourceClassName$3(service, ctx);
|
|
8643
8698
|
const serviceDir = resolveResourceDir(service, ctx);
|
|
8644
8699
|
const serviceClass = resolvedName;
|
|
8645
8700
|
const serviceProp = mountAccessors?.get(service.name) ?? servicePropertyName$4(resolveServiceName(service, ctx));
|
|
8646
8701
|
const testPath = `src/${serviceDir}/${fileName$3(resolvedName)}.spec.ts`;
|
|
8647
|
-
const
|
|
8702
|
+
const allPlans = service.operations.map((op) => ({
|
|
8648
8703
|
op,
|
|
8649
|
-
plan:
|
|
8704
|
+
plan: planOperationFor(op, ctx),
|
|
8650
8705
|
method: resolveMethodName$6(op, service, ctx)
|
|
8651
8706
|
}));
|
|
8707
|
+
const resolvedLookup = buildResolvedLookup(ctx);
|
|
8708
|
+
const preservedScopes = methodsWithPreservedTestBlocks(ctx, testPath);
|
|
8709
|
+
const plans = allPlans.filter((p) => {
|
|
8710
|
+
if (preservedScopes.has(p.method)) return true;
|
|
8711
|
+
if (ignoredMethodNames.has(p.method)) return false;
|
|
8712
|
+
if (lookupResolved(p.op, resolvedLookup)?.urlBuilder) return false;
|
|
8713
|
+
return true;
|
|
8714
|
+
});
|
|
8652
8715
|
if (ctx.overlayLookup?.methodByOperation) {
|
|
8653
8716
|
const methodOrder = /* @__PURE__ */ new Map();
|
|
8654
8717
|
let pos = 0;
|
|
@@ -8695,7 +8758,7 @@ function generateServiceTest$3(service, spec, ctx, modelMap, mountAccessors) {
|
|
|
8695
8758
|
lines.push("");
|
|
8696
8759
|
lines.push("const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');");
|
|
8697
8760
|
lines.push("");
|
|
8698
|
-
const { lines: helperLines, helpers: entityHelperNames } = generateEntityHelpers(plans, modelMap, ctx);
|
|
8761
|
+
const { lines: helperLines, helpers: entityHelperNames } = generateEntityHelpers(service, allPlans, plans, existingTestIgnoreText(ctx, testPath), modelMap, ctx);
|
|
8699
8762
|
for (const line of helperLines) lines.push(line);
|
|
8700
8763
|
lines.push(`describe('${serviceClass}', () => {`);
|
|
8701
8764
|
lines.push(" beforeEach(() => fetch.resetMocks());");
|
|
@@ -8711,6 +8774,20 @@ function generateServiceTest$3(service, spec, ctx, modelMap, mountAccessors) {
|
|
|
8711
8774
|
lines.push(" });");
|
|
8712
8775
|
}
|
|
8713
8776
|
lines.push("});");
|
|
8777
|
+
const body = lines.join("\n");
|
|
8778
|
+
const enumImportLines = [];
|
|
8779
|
+
for (const [name, info] of Object.entries(ctx.apiSurface?.enums ?? {})) {
|
|
8780
|
+
if (!new RegExp(`\\b${name}\\.[A-Za-z_$]`).test(body)) continue;
|
|
8781
|
+
if (new RegExp(`import\\b[^;]*\\b${name}\\b[^;]*from`).test(body)) continue;
|
|
8782
|
+
const sourceFile = info.sourceFile;
|
|
8783
|
+
const spec = sourceFile ? relativeImport(testPath, sourceFile).replace(/\.ts$/, "") : "./interfaces";
|
|
8784
|
+
enumImportLines.push(`import { ${name} } from '${spec}';`);
|
|
8785
|
+
}
|
|
8786
|
+
if (enumImportLines.length > 0) {
|
|
8787
|
+
const anchor = lines.indexOf("import { WorkOS } from '../workos';");
|
|
8788
|
+
const at = anchor >= 0 ? anchor + 1 : 0;
|
|
8789
|
+
lines.splice(at, 0, ...enumImportLines);
|
|
8790
|
+
}
|
|
8714
8791
|
return {
|
|
8715
8792
|
path: testPath,
|
|
8716
8793
|
content: lines.join("\n"),
|
|
@@ -8729,11 +8806,81 @@ function pathParamTestValue(param, paramName) {
|
|
|
8729
8806
|
if (name) return `test_${fieldName$6(name)}`;
|
|
8730
8807
|
return "test_id";
|
|
8731
8808
|
}
|
|
8732
|
-
|
|
8809
|
+
/** Render an example value as a valid TS literal expression.
|
|
8810
|
+
* Objects and nested arrays go through JSON.stringify so map-typed params
|
|
8811
|
+
* (e.g. providerQueryParams) don't coerce to the literal text `[object Object]`.
|
|
8812
|
+
*/
|
|
8813
|
+
function renderExampleLiteral(value) {
|
|
8814
|
+
if (typeof value === "string") return `'${value}'`;
|
|
8815
|
+
if (Array.isArray(value)) return `[${value.map(renderExampleLiteral).join(", ")}]`;
|
|
8816
|
+
if (value !== null && typeof value === "object") return JSON.stringify(value);
|
|
8817
|
+
return String(value);
|
|
8818
|
+
}
|
|
8819
|
+
/** Resolve the enum members a query-param test value must satisfy. An enum is
|
|
8820
|
+
* expressed several ways: an inline `EnumRef` with `values`, an `enum`/`model`
|
|
8821
|
+
* ref by name, or — when the IR flattened the param to a bare string but the
|
|
8822
|
+
* hand-written options interface still types the field as an enum — the
|
|
8823
|
+
* baseline options field type (e.g. `ConnectionType`). */
|
|
8824
|
+
/** All valid values for an enum named `name`. The extracted SDK surface is
|
|
8825
|
+
* consulted first: when an options field is typed with a hand-written enum
|
|
8826
|
+
* (e.g. `ConnectionType` from `connection-type.enum.ts`), that enum — not a
|
|
8827
|
+
* same-named IR enum that may carry extra members like `Pending` — is what
|
|
8828
|
+
* the generated test must type-check against. Falls back to the IR spec. */
|
|
8829
|
+
function enumValuesByName(name, ctx) {
|
|
8830
|
+
const members = ctx?.apiSurface?.enums?.[name]?.members;
|
|
8831
|
+
if (members && Object.keys(members).length > 0) return Object.values(members);
|
|
8832
|
+
const specValues = ctx?.spec.enums.find((e) => e.name === name)?.values;
|
|
8833
|
+
if (specValues?.length) return specValues.map((v) => v.value);
|
|
8834
|
+
}
|
|
8835
|
+
function resolveParamEnumValues(param, optionFieldType, ctx) {
|
|
8836
|
+
if (optionFieldType) {
|
|
8837
|
+
const bare = optionFieldType.replace(/\[\]/g, "").replace(/\|\s*(undefined|null)/g, "").trim();
|
|
8838
|
+
if (/^[A-Za-z_$][\w$]*$/.test(bare)) {
|
|
8839
|
+
const values = enumValuesByName(bare, ctx);
|
|
8840
|
+
if (values) return values;
|
|
8841
|
+
}
|
|
8842
|
+
}
|
|
8843
|
+
if ((param.type.kind === "enum" || param.type.kind === "model") && param.type.name) {
|
|
8844
|
+
const values = enumValuesByName(param.type.name, ctx);
|
|
8845
|
+
if (values) return values;
|
|
8846
|
+
}
|
|
8847
|
+
if (param.type.kind === "enum" && param.type.values?.length) return param.type.values;
|
|
8848
|
+
}
|
|
8849
|
+
/** When an options field is typed with a real (nominal) TS `enum` from the SDK
|
|
8850
|
+
* surface, a string literal won't type-check — the value must be a member
|
|
8851
|
+
* reference (`ConnectionType.ADFSSAML`). Returns the enum's name + members so
|
|
8852
|
+
* the caller can both render the reference and import the right declaration. */
|
|
8853
|
+
function resolveRealEnum(param, optionFieldType, ctx) {
|
|
8854
|
+
const candidates = [];
|
|
8855
|
+
if (optionFieldType) {
|
|
8856
|
+
const bare = optionFieldType.replace(/\[\]/g, "").replace(/\|\s*(undefined|null)/g, "").trim();
|
|
8857
|
+
if (/^[A-Za-z_$][\w$]*$/.test(bare)) candidates.push(bare);
|
|
8858
|
+
}
|
|
8859
|
+
if ((param.type.kind === "enum" || param.type.kind === "model") && param.type.name) candidates.push(param.type.name);
|
|
8860
|
+
for (const name of candidates) {
|
|
8861
|
+
const members = ctx?.apiSurface?.enums?.[name]?.members;
|
|
8862
|
+
if (members && Object.keys(members).length > 0) return {
|
|
8863
|
+
name,
|
|
8864
|
+
members
|
|
8865
|
+
};
|
|
8866
|
+
}
|
|
8867
|
+
return null;
|
|
8868
|
+
}
|
|
8869
|
+
function queryParamTestValue(param, modelMap, ctx, optionFieldType) {
|
|
8870
|
+
const realEnum = resolveRealEnum(param, optionFieldType, ctx);
|
|
8871
|
+
if (realEnum) {
|
|
8872
|
+
const entries = Object.entries(realEnum.members);
|
|
8873
|
+
const [memberKey] = (typeof param.example === "string" ? entries.find(([, v]) => v === param.example) : void 0) ?? entries[0];
|
|
8874
|
+
return `${realEnum.name}.${memberKey}`;
|
|
8875
|
+
}
|
|
8876
|
+
const enumValues = resolveParamEnumValues(param, optionFieldType, ctx);
|
|
8877
|
+
if (enumValues?.length) {
|
|
8878
|
+
const valid = typeof param.example === "string" && enumValues.includes(param.example) ? param.example : enumValues[0];
|
|
8879
|
+
return typeof valid === "string" ? `'${valid}'` : String(valid);
|
|
8880
|
+
}
|
|
8733
8881
|
if (param.example !== void 0) {
|
|
8734
|
-
if (Array.isArray(param.example)) return `[${param.example.map((v) => typeof v === "string" ? `'${v}'` : String(v)).join(", ")}]`;
|
|
8735
8882
|
if (param.type.kind === "primitive" && param.type.format === "date-time" && typeof param.example === "string") return `new Date('${param.example}')`;
|
|
8736
|
-
return
|
|
8883
|
+
return renderExampleLiteral(param.example);
|
|
8737
8884
|
}
|
|
8738
8885
|
return fixtureValueForType(param.type, param.name, "Options", modelMap) ?? "'test'";
|
|
8739
8886
|
}
|
|
@@ -8899,9 +9046,10 @@ function buildOptionsObjectTestArg(op, plan, baselineMethod, modelMap, ctx) {
|
|
|
8899
9046
|
const optionParam = optionsObjectParam(baselineMethod);
|
|
8900
9047
|
if (!optionParam) return null;
|
|
8901
9048
|
const entries = [];
|
|
9049
|
+
const pathFieldMap = ctx ? operationOverrideFor(ctx, op)?.pathFieldMap : void 0;
|
|
8902
9050
|
for (const param of op.pathParams) {
|
|
8903
9051
|
const localName = fieldName$6(param.name);
|
|
8904
|
-
const optionField = resolveOptionsObjectField(localName, optionParam.type, ctx);
|
|
9052
|
+
const optionField = resolveOptionsObjectField(localName, optionParam.type, ctx, pathFieldMap);
|
|
8905
9053
|
entries.push(`${optionField}: ${JSON.stringify(pathParamTestValue(param, localName))}`);
|
|
8906
9054
|
}
|
|
8907
9055
|
if (plan.isPaginated) entries.push("order: 'desc'");
|
|
@@ -8913,7 +9061,8 @@ function buildOptionsObjectTestArg(op, plan, baselineMethod, modelMap, ctx) {
|
|
|
8913
9061
|
].includes(param.name)) : op.queryParams).filter((param) => !param.deprecated);
|
|
8914
9062
|
for (const param of queryParams) {
|
|
8915
9063
|
const localName = fieldName$6(param.name);
|
|
8916
|
-
const
|
|
9064
|
+
const optionFieldType = ctx?.apiSurface?.interfaces?.[optionParam.type]?.fields?.[localName]?.type;
|
|
9065
|
+
const value = queryParamTestValue(param, modelMap, ctx, optionFieldType);
|
|
8917
9066
|
entries.push(`${localName}: ${value}`);
|
|
8918
9067
|
}
|
|
8919
9068
|
if (plan.hasBody) {
|
|
@@ -8941,7 +9090,9 @@ function objectLiteralEntries(literal) {
|
|
|
8941
9090
|
const body = trimmed.slice(1, -1).trim();
|
|
8942
9091
|
return body ? body.split(",").map((entry) => entry.trim()) : [];
|
|
8943
9092
|
}
|
|
8944
|
-
function resolveOptionsObjectField(localName, optionType, ctx) {
|
|
9093
|
+
function resolveOptionsObjectField(localName, optionType, ctx, pathFieldMap) {
|
|
9094
|
+
const mapped = pathFieldMap?.[localName];
|
|
9095
|
+
if (mapped) return mapped;
|
|
8945
9096
|
const fields = ctx?.apiSurface?.interfaces?.[optionType]?.fields;
|
|
8946
9097
|
if (!fields) return localName;
|
|
8947
9098
|
if (fields[localName]) return localName;
|
|
@@ -8956,20 +9107,85 @@ function resolveOptionsObjectField(localName, optionType, ctx) {
|
|
|
8956
9107
|
* Generate per-entity assertion helper functions for models used in 2+ tests.
|
|
8957
9108
|
* Returns { lines, helpers } where helpers is a Set of helper function names.
|
|
8958
9109
|
*/
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
9110
|
+
/** Describe-scope names (i.e. method names) that have an `@oagen-ignore` block
|
|
9111
|
+
* nested inside them in the existing test file. Such a `describe` must keep
|
|
9112
|
+
* being emitted even when the method is hand-owned — otherwise the engine has
|
|
9113
|
+
* no `describe` to re-nest the preserved block under and orphans it to the top
|
|
9114
|
+
* of the file (out of `beforeEach` scope), which hangs at runtime. Mirrors the
|
|
9115
|
+
* engine's `findContainingDescribeScope`. */
|
|
9116
|
+
function methodsWithPreservedTestBlocks(ctx, relPath) {
|
|
9117
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
9118
|
+
const root = ctx.outputDir ?? ctx.targetDir;
|
|
9119
|
+
if (!root) return scopes;
|
|
9120
|
+
let content;
|
|
9121
|
+
try {
|
|
9122
|
+
content = fs.readFileSync(path.join(root, relPath), "utf8");
|
|
9123
|
+
} catch {
|
|
9124
|
+
return scopes;
|
|
9125
|
+
}
|
|
9126
|
+
const lines = content.split("\n");
|
|
9127
|
+
const indentOf = (l) => l.length - l.trimStart().length;
|
|
9128
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9129
|
+
if (!lines[i].includes("@oagen-ignore-start")) continue;
|
|
9130
|
+
if (/^\S/.test(lines[i])) continue;
|
|
9131
|
+
const markerIndent = indentOf(lines[i]);
|
|
9132
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
9133
|
+
if (indentOf(lines[j]) >= markerIndent) continue;
|
|
9134
|
+
const m = lines[j].match(/^\s*describe\((['"`])(.+?)\1\s*,/);
|
|
9135
|
+
if (m) {
|
|
9136
|
+
scopes.add(m[2]);
|
|
9137
|
+
break;
|
|
9138
|
+
}
|
|
9139
|
+
if (/^\s*(?:export\s+)?class\s+\w+/.test(lines[j])) break;
|
|
9140
|
+
}
|
|
9141
|
+
}
|
|
9142
|
+
return scopes;
|
|
9143
|
+
}
|
|
9144
|
+
/** Concatenated text of the existing test file's `@oagen-ignore` regions, so
|
|
9145
|
+
* helper generation can see which `expect<Model>()` helpers preserved
|
|
9146
|
+
* hand-written test blocks still reference. */
|
|
9147
|
+
function existingTestIgnoreText(ctx, relPath) {
|
|
9148
|
+
const root = ctx.outputDir ?? ctx.targetDir;
|
|
9149
|
+
if (!root) return "";
|
|
9150
|
+
let content;
|
|
9151
|
+
try {
|
|
9152
|
+
content = fs.readFileSync(path.join(root, relPath), "utf8");
|
|
9153
|
+
} catch {
|
|
9154
|
+
return "";
|
|
9155
|
+
}
|
|
9156
|
+
return [...content.matchAll(/@oagen-ignore-start[\s\S]*?@oagen-ignore-end/g)].map((m) => m[0]).join("\n");
|
|
9157
|
+
}
|
|
9158
|
+
function generateEntityHelpers(service, allPlans, renderedPlans, ignoreRegionText, modelMap, ctx) {
|
|
9159
|
+
const responseModelOf = (entry) => {
|
|
9160
|
+
const { op, plan } = entry;
|
|
8963
9161
|
if (plan.isPaginated && op.pagination?.itemType.kind === "model") {
|
|
8964
|
-
modelName = op.pagination.itemType.name;
|
|
9162
|
+
let modelName = op.pagination.itemType.name;
|
|
8965
9163
|
const rawModel = modelMap.get(modelName);
|
|
8966
9164
|
if (rawModel) {
|
|
8967
9165
|
const unwrapped = unwrapListModel$4(rawModel, modelMap);
|
|
8968
9166
|
if (unwrapped) modelName = unwrapped.name;
|
|
8969
9167
|
}
|
|
8970
|
-
|
|
9168
|
+
return modelName;
|
|
9169
|
+
}
|
|
9170
|
+
return plan.responseModelName ?? null;
|
|
9171
|
+
};
|
|
9172
|
+
const modelUsage = /* @__PURE__ */ new Map();
|
|
9173
|
+
for (const entry of allPlans) {
|
|
9174
|
+
const modelName = responseModelOf(entry);
|
|
8971
9175
|
if (modelName) modelUsage.set(modelName, (modelUsage.get(modelName) ?? 0) + 1);
|
|
8972
9176
|
}
|
|
9177
|
+
const renderedModels = /* @__PURE__ */ new Set();
|
|
9178
|
+
for (const entry of renderedPlans) {
|
|
9179
|
+
const modelName = responseModelOf(entry);
|
|
9180
|
+
if (!modelName) continue;
|
|
9181
|
+
if (entry.plan.isPaginated && entry.op.pagination?.itemType.kind === "model") {
|
|
9182
|
+
const baselineMethod = optionsMethodFor(service, entry.method, entry.op, entry.plan, ctx);
|
|
9183
|
+
const baselineItemType = autoPaginatableItemType(baselineMethod?.returnType);
|
|
9184
|
+
const generatedItemType = resolveInterfaceName(modelName, ctx);
|
|
9185
|
+
if (Boolean(baselineMethod?.returnType && !baselineItemType) || Boolean(baselineItemType && generatedItemType && baselineItemType !== generatedItemType)) continue;
|
|
9186
|
+
}
|
|
9187
|
+
renderedModels.add(modelName);
|
|
9188
|
+
}
|
|
8973
9189
|
const lines = [];
|
|
8974
9190
|
const helpers = /* @__PURE__ */ new Set();
|
|
8975
9191
|
for (const [modelName, count] of modelUsage) {
|
|
@@ -8980,6 +9196,7 @@ function generateEntityHelpers(plans, modelMap, ctx) {
|
|
|
8980
9196
|
if (assertions.length === 0) continue;
|
|
8981
9197
|
const helperName = `expect${resolveInterfaceName(modelName, ctx)}`;
|
|
8982
9198
|
if (helpers.has(helperName)) continue;
|
|
9199
|
+
if (!(renderedModels.has(modelName) || ignoreRegionText.includes(`${helperName}(`))) continue;
|
|
8983
9200
|
helpers.add(helperName);
|
|
8984
9201
|
lines.push(`function ${helperName}(result: any) {`);
|
|
8985
9202
|
for (const assertion of assertions) lines.push(` ${assertion}`);
|
|
@@ -9009,7 +9226,7 @@ function buildFieldAssertions(model, accessor, modelMap) {
|
|
|
9009
9226
|
const assertions = [];
|
|
9010
9227
|
for (const field of model.fields) {
|
|
9011
9228
|
if (!field.required) continue;
|
|
9012
|
-
const domainField = fieldName$6(field.name);
|
|
9229
|
+
const domainField = fieldName$6(field.domainName ?? field.name);
|
|
9013
9230
|
const isDateTime = isDateTimeFieldType(field.type);
|
|
9014
9231
|
const fieldAccessor = isDateTime ? `${accessor}.${domainField}.toISOString()` : `${accessor}.${domainField}`;
|
|
9015
9232
|
if (field.example !== void 0) {
|
|
@@ -10075,6 +10292,21 @@ function withNodeOperationOverrides(ctx) {
|
|
|
10075
10292
|
return next;
|
|
10076
10293
|
}
|
|
10077
10294
|
//#endregion
|
|
10295
|
+
//#region src/shared/file-header.ts
|
|
10296
|
+
/**
|
|
10297
|
+
* Canonical "do not edit" banner text shared by every emitter's
|
|
10298
|
+
* `fileHeader()`. The text is comment-syntax agnostic — each emitter prefixes
|
|
10299
|
+
* it with the appropriate comment marker for its language (`//`, `#`, etc.).
|
|
10300
|
+
*
|
|
10301
|
+
* Keeping this in one place prevents the wording from drifting between
|
|
10302
|
+
* languages (Rust previously emitted a different banner).
|
|
10303
|
+
*
|
|
10304
|
+
* Go intentionally does NOT use this constant: its header must match the
|
|
10305
|
+
* standard `^// Code generated .* DO NOT EDIT\.$` marker that Go tooling
|
|
10306
|
+
* relies on to recognize generated files. See `src/go/index.ts`.
|
|
10307
|
+
*/
|
|
10308
|
+
const AUTOGEN_NOTICE = "This file is auto-generated by oagen. Do not edit.";
|
|
10309
|
+
//#endregion
|
|
10078
10310
|
//#region src/node/index.ts
|
|
10079
10311
|
/**
|
|
10080
10312
|
* Cache live-surface per ctx — every emitter method receives the same ctx in
|
|
@@ -10620,7 +10852,7 @@ const nodeEmitter = {
|
|
|
10620
10852
|
return {};
|
|
10621
10853
|
},
|
|
10622
10854
|
fileHeader() {
|
|
10623
|
-
return
|
|
10855
|
+
return `// ${AUTOGEN_NOTICE}`;
|
|
10624
10856
|
},
|
|
10625
10857
|
formatCommand(targetDir) {
|
|
10626
10858
|
const hasPrettier = fs$1.existsSync(path$1.join(targetDir, ".prettierrc"));
|
|
@@ -10688,6 +10920,15 @@ function fieldName$5(name) {
|
|
|
10688
10920
|
return toSnakeCase(name);
|
|
10689
10921
|
}
|
|
10690
10922
|
/**
|
|
10923
|
+
* snake_case domain field name for a model field, honoring a `domainName`
|
|
10924
|
+
* override (set via the `fieldHints` config) so a wire field can surface under
|
|
10925
|
+
* a friendlier name. The wire name (still derived from `field.name`) is
|
|
10926
|
+
* unaffected, so the API contract is preserved.
|
|
10927
|
+
*/
|
|
10928
|
+
function domainFieldName$5(field) {
|
|
10929
|
+
return toSnakeCase(field.domainName ?? field.name);
|
|
10930
|
+
}
|
|
10931
|
+
/**
|
|
10691
10932
|
* Python builtins that should not be shadowed by parameter names.
|
|
10692
10933
|
* When a path/query param name collides, suffix with underscore.
|
|
10693
10934
|
*/
|
|
@@ -11585,7 +11826,7 @@ function generateModels$6(models, ctx) {
|
|
|
11585
11826
|
}
|
|
11586
11827
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
11587
11828
|
const deduplicatedFields = model.fields.filter((f) => {
|
|
11588
|
-
const pyName =
|
|
11829
|
+
const pyName = domainFieldName$5(f);
|
|
11589
11830
|
if (seenFieldNames.has(pyName)) return false;
|
|
11590
11831
|
seenFieldNames.add(pyName);
|
|
11591
11832
|
return true;
|
|
@@ -11647,7 +11888,7 @@ function generateModels$6(models, ctx) {
|
|
|
11647
11888
|
return typeStr;
|
|
11648
11889
|
};
|
|
11649
11890
|
for (const field of requiredFields) {
|
|
11650
|
-
const pyFieldName =
|
|
11891
|
+
const pyFieldName = domainFieldName$5(field);
|
|
11651
11892
|
const pyType = rewriteDiscriminatorType(resolveModelFieldType(field.type));
|
|
11652
11893
|
if (field.description || field.deprecated) {
|
|
11653
11894
|
const parts = [];
|
|
@@ -11658,7 +11899,7 @@ function generateModels$6(models, ctx) {
|
|
|
11658
11899
|
} else lines.push(` ${pyFieldName}: ${pyType}`);
|
|
11659
11900
|
}
|
|
11660
11901
|
for (const field of optionalFields) {
|
|
11661
|
-
const pyFieldName =
|
|
11902
|
+
const pyFieldName = domainFieldName$5(field);
|
|
11662
11903
|
const pyType = `Optional[${rewriteDiscriminatorType(field.type.kind === "nullable" ? resolveModelFieldType(field.type.inner) : resolveModelFieldType(field.type))}]`;
|
|
11663
11904
|
if (field.description || field.deprecated) {
|
|
11664
11905
|
const parts = [];
|
|
@@ -11676,7 +11917,7 @@ function generateModels$6(models, ctx) {
|
|
|
11676
11917
|
const preludeLines = [];
|
|
11677
11918
|
const fieldAssignmentLines = [];
|
|
11678
11919
|
for (const field of [...requiredFields, ...optionalFields]) {
|
|
11679
|
-
const pyFieldName =
|
|
11920
|
+
const pyFieldName = domainFieldName$5(field);
|
|
11680
11921
|
const wireKey = field.name;
|
|
11681
11922
|
const isRequired = !isOptionalField(model.name, field, ctx);
|
|
11682
11923
|
const discPrelude = renderDiscriminatedUnionPrelude(field, pyFieldName, wireKey, modelClassName, isRequired);
|
|
@@ -11704,7 +11945,7 @@ function generateModels$6(models, ctx) {
|
|
|
11704
11945
|
lines.push(" \"\"\"Serialize to a dictionary.\"\"\"");
|
|
11705
11946
|
lines.push(" result: Dict[str, Any] = {}");
|
|
11706
11947
|
for (const field of [...requiredFields, ...optionalFields]) {
|
|
11707
|
-
const pyFieldName =
|
|
11948
|
+
const pyFieldName = domainFieldName$5(field);
|
|
11708
11949
|
const wireKey = field.name;
|
|
11709
11950
|
const isRequired = !isOptionalField(model.name, field, ctx);
|
|
11710
11951
|
const isNullable = field.type.kind === "nullable";
|
|
@@ -13580,7 +13821,7 @@ function generateModelFixture$4(model, modelMap, enumMap) {
|
|
|
13580
13821
|
const fixture = {};
|
|
13581
13822
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
13582
13823
|
const deduplicatedFields = model.fields.filter((f) => {
|
|
13583
|
-
const pyName =
|
|
13824
|
+
const pyName = domainFieldName$5(f);
|
|
13584
13825
|
if (seenFieldNames.has(pyName)) return false;
|
|
13585
13826
|
seenFieldNames.add(pyName);
|
|
13586
13827
|
return true;
|
|
@@ -14376,16 +14617,16 @@ function pickAssertableFields(modelName, spec, maxFields = 2) {
|
|
|
14376
14617
|
if (typeof val === "string") {
|
|
14377
14618
|
if (val.includes("\"") || val.includes("'") || val.includes("{") || val.includes("\\") || val.includes("\n")) continue;
|
|
14378
14619
|
results.push({
|
|
14379
|
-
field:
|
|
14620
|
+
field: domainFieldName$5(f),
|
|
14380
14621
|
value: `"${val}"`
|
|
14381
14622
|
});
|
|
14382
14623
|
} else if (typeof val === "boolean") results.push({
|
|
14383
|
-
field:
|
|
14624
|
+
field: domainFieldName$5(f),
|
|
14384
14625
|
value: val ? "True" : "False",
|
|
14385
14626
|
isBool: true
|
|
14386
14627
|
});
|
|
14387
14628
|
else if (typeof val === "number") results.push({
|
|
14388
|
-
field:
|
|
14629
|
+
field: domainFieldName$5(f),
|
|
14389
14630
|
value: String(val)
|
|
14390
14631
|
});
|
|
14391
14632
|
}
|
|
@@ -14445,7 +14686,7 @@ function buildTestArgs$1(op, spec, hiddenParams) {
|
|
|
14445
14686
|
].includes(param.name)) continue;
|
|
14446
14687
|
if (plan.hasBody && op.requestBody?.kind === "model") {
|
|
14447
14688
|
const rbName = op.requestBody.name;
|
|
14448
|
-
if (spec.models.find((m) => m.name === rbName)?.fields.some((f) =>
|
|
14689
|
+
if (spec.models.find((m) => m.name === rbName)?.fields.some((f) => domainFieldName$5(f) === fieldName$5(param.name))) continue;
|
|
14449
14690
|
}
|
|
14450
14691
|
if (param.required && !pathParamNames.has(fieldName$5(param.name))) args.push(`${fieldName$5(param.name)}=${generateTestValue$1(param.type, param.name)}`);
|
|
14451
14692
|
}
|
|
@@ -14704,7 +14945,7 @@ function generateModelRoundTripTests(spec, ctx) {
|
|
|
14704
14945
|
if (model.discriminator) continue;
|
|
14705
14946
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
14706
14947
|
const dedupFields = model.fields.filter((f) => {
|
|
14707
|
-
const pyName =
|
|
14948
|
+
const pyName = domainFieldName$5(f);
|
|
14708
14949
|
if (seenFieldNames.has(pyName)) return false;
|
|
14709
14950
|
seenFieldNames.add(pyName);
|
|
14710
14951
|
return true;
|
|
@@ -14896,7 +15137,7 @@ const pythonEmitter = {
|
|
|
14896
15137
|
return buildOperationsMap$6(spec, ctx);
|
|
14897
15138
|
},
|
|
14898
15139
|
fileHeader() {
|
|
14899
|
-
return
|
|
15140
|
+
return `# ${AUTOGEN_NOTICE}`;
|
|
14900
15141
|
},
|
|
14901
15142
|
formatCommand(_targetDir) {
|
|
14902
15143
|
return {
|
|
@@ -14997,6 +15238,15 @@ function resolveMethodName$4(op, _service, ctx) {
|
|
|
14997
15238
|
function fieldName$4(name) {
|
|
14998
15239
|
return toCamelCase(name);
|
|
14999
15240
|
}
|
|
15241
|
+
/**
|
|
15242
|
+
* camelCase DOMAIN property name for a model field, honoring a `domainName`
|
|
15243
|
+
* override (set via the `fieldHints` config) so a wire field can surface under
|
|
15244
|
+
* a friendlier PHP property name. The wire key (see {@link wireName}) still
|
|
15245
|
+
* derives from `field.name`. No-op when `domainName` is unset.
|
|
15246
|
+
*/
|
|
15247
|
+
function domainFieldName$4(field) {
|
|
15248
|
+
return fieldName$4(field.domainName ?? field.name);
|
|
15249
|
+
}
|
|
15000
15250
|
/** snake_case name for fixtures and other snake_case contexts. */
|
|
15001
15251
|
function snakeName(name) {
|
|
15002
15252
|
return toSnakeCase(stripUrnPrefix(name));
|
|
@@ -15131,14 +15381,14 @@ function generateModels$5(models, ctx) {
|
|
|
15131
15381
|
const optionalFields = model.fields.filter((f) => !f.required);
|
|
15132
15382
|
const seenNames = /* @__PURE__ */ new Set();
|
|
15133
15383
|
const allFields = [...requiredFields, ...optionalFields].filter((f) => {
|
|
15134
|
-
const phpName =
|
|
15384
|
+
const phpName = domainFieldName$4(f);
|
|
15135
15385
|
if (seenNames.has(phpName)) return false;
|
|
15136
15386
|
seenNames.add(phpName);
|
|
15137
15387
|
return true;
|
|
15138
15388
|
});
|
|
15139
15389
|
for (let i = 0; i < allFields.length; i++) {
|
|
15140
15390
|
const field = allFields[i];
|
|
15141
|
-
const phpName =
|
|
15391
|
+
const phpName = domainFieldName$4(field);
|
|
15142
15392
|
const phpType = mapTypeRef$5(field.type);
|
|
15143
15393
|
const isOptional = !field.required;
|
|
15144
15394
|
const comma = i < allFields.length - 1 ? "," : ",";
|
|
@@ -15165,7 +15415,7 @@ function generateModels$5(models, ctx) {
|
|
|
15165
15415
|
lines.push(` return new self(`);
|
|
15166
15416
|
for (let i = 0; i < allFields.length; i++) {
|
|
15167
15417
|
const field = allFields[i];
|
|
15168
|
-
const phpName =
|
|
15418
|
+
const phpName = domainFieldName$4(field);
|
|
15169
15419
|
const wireName = field.name;
|
|
15170
15420
|
const comma = i < allFields.length - 1 ? "," : ",";
|
|
15171
15421
|
const accessor = generateFromArrayAccessor(field.type, wireName, field.required);
|
|
@@ -15178,7 +15428,7 @@ function generateModels$5(models, ctx) {
|
|
|
15178
15428
|
lines.push(" {");
|
|
15179
15429
|
lines.push(" return [");
|
|
15180
15430
|
for (const field of allFields) {
|
|
15181
|
-
const phpName =
|
|
15431
|
+
const phpName = domainFieldName$4(field);
|
|
15182
15432
|
const wireName = field.name;
|
|
15183
15433
|
const serialized = generateToArrayValue(field.type, `$this->${phpName}`, !field.required);
|
|
15184
15434
|
lines.push(` '${wireName}' => ${serialized},`);
|
|
@@ -16602,7 +16852,7 @@ function emitFieldHydrationAssertions(lines, modelName, resultVar, fixtureVar, c
|
|
|
16602
16852
|
const assertFields = [candidates.find((f) => f.name === "id"), candidates.filter((f) => f.name !== "id")[0]].filter(Boolean);
|
|
16603
16853
|
for (const f of assertFields) {
|
|
16604
16854
|
if (!f) continue;
|
|
16605
|
-
const phpProp =
|
|
16855
|
+
const phpProp = domainFieldName$4(f);
|
|
16606
16856
|
lines.push(` $this->assertSame(${fixtureVar}['${f.name}'], ${resultVar}->${phpProp});`);
|
|
16607
16857
|
}
|
|
16608
16858
|
}
|
|
@@ -16782,7 +17032,7 @@ const phpEmitter = {
|
|
|
16782
17032
|
return buildOperationsMap$5(spec, ctx);
|
|
16783
17033
|
},
|
|
16784
17034
|
fileHeader() {
|
|
16785
|
-
return
|
|
17035
|
+
return `<?php\n\ndeclare(strict_types=1);\n\n// ${AUTOGEN_NOTICE}`;
|
|
16786
17036
|
},
|
|
16787
17037
|
formatCommand(targetDir) {
|
|
16788
17038
|
if (fs$1.existsSync(path$1.join(targetDir, ".php-cs-fixer.dist.php")) || fs$1.existsSync(path$1.join(targetDir, ".php-cs-fixer.php"))) return {
|
|
@@ -16845,6 +17095,15 @@ function methodName$3(name) {
|
|
|
16845
17095
|
function fieldName$3(name) {
|
|
16846
17096
|
return applyAcronyms(toPascalCase(name));
|
|
16847
17097
|
}
|
|
17098
|
+
/**
|
|
17099
|
+
* PascalCase domain field name for a model field, honoring a `domainName`
|
|
17100
|
+
* override (set via the `fieldHints` config) so a wire field can surface under
|
|
17101
|
+
* a friendlier name. The wire name (the `json:"..."` struct tag) still derives
|
|
17102
|
+
* from `field.name`.
|
|
17103
|
+
*/
|
|
17104
|
+
function domainFieldName$3(field) {
|
|
17105
|
+
return applyAcronyms(toPascalCase(field.domainName ?? field.name));
|
|
17106
|
+
}
|
|
16848
17107
|
/** Lower-camel identifier with Go acronym conventions preserved. */
|
|
16849
17108
|
function unexportedName(name) {
|
|
16850
17109
|
const exported = className$3(name);
|
|
@@ -17093,7 +17352,7 @@ function generateModels$4(models, ctx) {
|
|
|
17093
17352
|
lines.push(`type ${structName} struct {`);
|
|
17094
17353
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
17095
17354
|
for (const field of model.fields) {
|
|
17096
|
-
const goFieldName =
|
|
17355
|
+
const goFieldName = domainFieldName$3(field);
|
|
17097
17356
|
if (seenFieldNames.has(goFieldName)) continue;
|
|
17098
17357
|
seenFieldNames.add(goFieldName);
|
|
17099
17358
|
const goType = !field.required ? makeOptional$2(mapTypeRef$4(field.type)) : mapTypeRef$4(field.type);
|
|
@@ -17907,7 +18166,7 @@ function generateParamsStruct(mountName, method, op, plan, ctx, resolvedOp) {
|
|
|
17907
18166
|
if (bodyModel) for (const field of bodyModel.fields) {
|
|
17908
18167
|
if (hidden.has(field.name)) continue;
|
|
17909
18168
|
if (groupedParams.has(field.name)) continue;
|
|
17910
|
-
const goField =
|
|
18169
|
+
const goField = domainFieldName$3(field);
|
|
17911
18170
|
if (emittedFields.has(goField)) continue;
|
|
17912
18171
|
emittedFields.add(goField);
|
|
17913
18172
|
const goType = !field.required ? makeOptional$1(mapTypeRef$4(field.type)) : mapTypeRef$4(field.type);
|
|
@@ -18245,7 +18504,7 @@ function emitHiddenParamsBodyStruct(lines, method, op, ctx, resolvedOp) {
|
|
|
18245
18504
|
if (hidden.has(field.name)) continue;
|
|
18246
18505
|
if (groupedParamNames.has(field.name)) continue;
|
|
18247
18506
|
if (!field.required) continue;
|
|
18248
|
-
const goField =
|
|
18507
|
+
const goField = domainFieldName$3(field);
|
|
18249
18508
|
const goType = mapTypeRef$4(field.type);
|
|
18250
18509
|
lines.push(`\t${goField} ${goType} \`json:"${field.name}"\``);
|
|
18251
18510
|
}
|
|
@@ -18257,7 +18516,7 @@ function emitHiddenParamsBodyStruct(lines, method, op, ctx, resolvedOp) {
|
|
|
18257
18516
|
if (hidden.has(field.name)) continue;
|
|
18258
18517
|
if (groupedParamNames.has(field.name)) continue;
|
|
18259
18518
|
if (field.required) continue;
|
|
18260
|
-
const goField =
|
|
18519
|
+
const goField = domainFieldName$3(field);
|
|
18261
18520
|
const goType = makeOptional$1(mapTypeRef$4(field.type));
|
|
18262
18521
|
lines.push(`\t${goField} ${goType} \`json:"${field.name},omitempty"\``);
|
|
18263
18522
|
}
|
|
@@ -18278,7 +18537,7 @@ function emitBodyWithHiddenParams(lines, op, pathExpr, plan, ctx, resolvedOp, pa
|
|
|
18278
18537
|
if (paramsType && bodyModel) for (const field of bodyModel.fields) {
|
|
18279
18538
|
if (hidden.has(field.name)) continue;
|
|
18280
18539
|
if (!field.required) continue;
|
|
18281
|
-
const goField =
|
|
18540
|
+
const goField = domainFieldName$3(field);
|
|
18282
18541
|
lines.push(`\t\t${goField}: params.${goField},`);
|
|
18283
18542
|
}
|
|
18284
18543
|
lines.push(" }");
|
|
@@ -18289,7 +18548,7 @@ function emitBodyWithHiddenParams(lines, op, pathExpr, plan, ctx, resolvedOp, pa
|
|
|
18289
18548
|
if (paramsType && bodyModel) for (const field of bodyModel.fields) {
|
|
18290
18549
|
if (hidden.has(field.name)) continue;
|
|
18291
18550
|
if (field.required) continue;
|
|
18292
|
-
const goField =
|
|
18551
|
+
const goField = domainFieldName$3(field);
|
|
18293
18552
|
lines.push(`\tbody.${goField} = params.${goField}`);
|
|
18294
18553
|
}
|
|
18295
18554
|
const queryArg = op.queryParams.filter((qp) => !hidden.has(qp.name)).length > 0 ? "params" : "nil";
|
|
@@ -18716,7 +18975,7 @@ function generateModelFixture$2(model, modelMap, enumMap) {
|
|
|
18716
18975
|
const fixture = {};
|
|
18717
18976
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
18718
18977
|
const deduplicatedFields = model.fields.filter((f) => {
|
|
18719
|
-
const goName =
|
|
18978
|
+
const goName = domainFieldName$3(f);
|
|
18720
18979
|
if (seenFieldNames.has(goName)) return false;
|
|
18721
18980
|
seenFieldNames.add(goName);
|
|
18722
18981
|
return true;
|
|
@@ -19456,6 +19715,15 @@ function methodName$2(name) {
|
|
|
19456
19715
|
function fieldName$2(name) {
|
|
19457
19716
|
return toPascalCase(name);
|
|
19458
19717
|
}
|
|
19718
|
+
/**
|
|
19719
|
+
* PascalCase domain property name for a model field, honoring a `domainName`
|
|
19720
|
+
* override (set via the `fieldHints` config) so a wire field can surface under
|
|
19721
|
+
* a friendlier C# property name. The wire/serialization key (the
|
|
19722
|
+
* `[JsonPropertyName("...")]` value) still derives from `field.name`.
|
|
19723
|
+
*/
|
|
19724
|
+
function domainFieldName$2(field) {
|
|
19725
|
+
return toPascalCase(field.domainName ?? field.name);
|
|
19726
|
+
}
|
|
19459
19727
|
function trimAsyncSuffix(name) {
|
|
19460
19728
|
return name.endsWith("Async") ? name.slice(0, -5) : name;
|
|
19461
19729
|
}
|
|
@@ -19822,7 +20090,7 @@ function generateModels$3(models, ctx, discCtx) {
|
|
|
19822
20090
|
const baseClassName = modelClassName(model.name);
|
|
19823
20091
|
const fieldMap = /* @__PURE__ */ new Map();
|
|
19824
20092
|
for (const field of model.fields) {
|
|
19825
|
-
let csName =
|
|
20093
|
+
let csName = domainFieldName$2(field);
|
|
19826
20094
|
if (csName === baseClassName) csName = `${csName}Value`;
|
|
19827
20095
|
fieldMap.set(csName, mapTypeRef$3(field.type));
|
|
19828
20096
|
}
|
|
@@ -19838,7 +20106,9 @@ function generateModels$3(models, ctx, discCtx) {
|
|
|
19838
20106
|
const fieldTypes = model.fields.map((f) => mapTypeRef$3(f.type));
|
|
19839
20107
|
const needsCollections = fieldTypes.some((t) => t.startsWith("List<") || t.startsWith("Dictionary<"));
|
|
19840
20108
|
const needsSystem = fieldTypes.some((t) => t.includes("DateTimeOffset"));
|
|
19841
|
-
const
|
|
20109
|
+
const hasClassNameCollision = model.fields.some((f) => domainFieldName$2(f) === csClassName);
|
|
20110
|
+
const hasDomainRename = model.fields.some((f) => domainFieldName$2(f) !== fieldName$2(f.name));
|
|
20111
|
+
const needsJsonAttrs = hasClassNameCollision || hasDomainRename || model.fields.some((f) => f.required && isEnumRef(f.type));
|
|
19842
20112
|
lines.push(`namespace ${ctx.namespacePascal}`);
|
|
19843
20113
|
lines.push("{");
|
|
19844
20114
|
if (needsSystem) lines.push(" using System;");
|
|
@@ -19864,9 +20134,10 @@ function generateModels$3(models, ctx, discCtx) {
|
|
|
19864
20134
|
const dictObjectFields = [];
|
|
19865
20135
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
19866
20136
|
for (const field of model.fields) {
|
|
19867
|
-
let csFieldName =
|
|
20137
|
+
let csFieldName = domainFieldName$2(field);
|
|
19868
20138
|
const collidesWithClassName = csFieldName === csClassName;
|
|
19869
20139
|
if (collidesWithClassName) csFieldName = `${csFieldName}Value`;
|
|
20140
|
+
const hasDomainOverride = domainFieldName$2(field) !== fieldName$2(field.name);
|
|
19870
20141
|
if (seenFieldNames.has(csFieldName)) continue;
|
|
19871
20142
|
seenFieldNames.add(csFieldName);
|
|
19872
20143
|
let useNewModifier = false;
|
|
@@ -19913,7 +20184,7 @@ function generateModels$3(models, ctx, discCtx) {
|
|
|
19913
20184
|
const isRequiredEnum = field.required && isEnumRef(field.type) && constInit === null;
|
|
19914
20185
|
lines.push(...emitJsonPropertyAttributes(field.name, {
|
|
19915
20186
|
isRequiredEnum,
|
|
19916
|
-
explicitWireName: collidesWithClassName
|
|
20187
|
+
explicitWireName: collidesWithClassName || hasDomainOverride
|
|
19917
20188
|
}));
|
|
19918
20189
|
const discriminatedUnionConverter = discriminatedUnionConverterName(field.type);
|
|
19919
20190
|
if (discriminatedUnionConverter) lines.push(` [Newtonsoft.Json.JsonConverter(typeof(${discriminatedUnionConverter}))]`);
|
|
@@ -21219,7 +21490,7 @@ function generateModelFixture$1(model, modelMap, enumMap) {
|
|
|
21219
21490
|
const fixture = {};
|
|
21220
21491
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
21221
21492
|
const deduplicatedFields = model.fields.filter((f) => {
|
|
21222
|
-
const csName =
|
|
21493
|
+
const csName = domainFieldName$2(f);
|
|
21223
21494
|
if (seenFieldNames.has(csName)) return false;
|
|
21224
21495
|
seenFieldNames.add(csName);
|
|
21225
21496
|
return true;
|
|
@@ -21740,7 +22011,7 @@ function buildFixtureAssertions(model, spec) {
|
|
|
21740
22011
|
if (field.type.kind !== "primitive" || field.type.type !== "string") continue;
|
|
21741
22012
|
if (field.type.format === "date-time" || field.type.format === "date") continue;
|
|
21742
22013
|
if (field.type.format === "binary") continue;
|
|
21743
|
-
const csField =
|
|
22014
|
+
const csField = domainFieldName$2(field);
|
|
21744
22015
|
const val = fixture[field.name];
|
|
21745
22016
|
if (typeof val === "string" && val.length > 0) assertions.push(`Assert.Equal(${csStringLiteral(val)}, result.${csField});`);
|
|
21746
22017
|
else assertions.push(`Assert.NotEmpty(result.${csField});`);
|
|
@@ -21999,7 +22270,7 @@ const dotnetEmitter = {
|
|
|
21999
22270
|
return buildOperationsMap$3(spec, fixNamespace(ctx));
|
|
22000
22271
|
},
|
|
22001
22272
|
fileHeader() {
|
|
22002
|
-
return
|
|
22273
|
+
return `// ${AUTOGEN_NOTICE}`;
|
|
22003
22274
|
},
|
|
22004
22275
|
formatCommand(targetDir) {
|
|
22005
22276
|
const workspace = findDotnetWorkspace(targetDir);
|
|
@@ -22062,6 +22333,16 @@ function propertyName(name) {
|
|
|
22062
22333
|
if (camel === "object") return "objectType";
|
|
22063
22334
|
return escapeReserved(camel);
|
|
22064
22335
|
}
|
|
22336
|
+
/**
|
|
22337
|
+
* camelCase domain property name for a MODEL field, honoring a `domainName`
|
|
22338
|
+
* override (set via the `fieldHints` config) so a wire field can surface under
|
|
22339
|
+
* a friendlier name. The wire key (the `@JsonProperty("...")` argument) still
|
|
22340
|
+
* derives from `field.name`. No-op when `domainName` is unset, so it is also
|
|
22341
|
+
* safe on params. Only apply to model fields.
|
|
22342
|
+
*/
|
|
22343
|
+
function domainPropertyName(field) {
|
|
22344
|
+
return propertyName(field.domainName ?? field.name);
|
|
22345
|
+
}
|
|
22065
22346
|
/** Lower-case Kotlin package segment for a service / mount group. */
|
|
22066
22347
|
function packageSegment(name) {
|
|
22067
22348
|
return toPascalCase(name).toLowerCase();
|
|
@@ -22764,7 +23045,7 @@ function renderFields(fields, overrideFields = /* @__PURE__ */ new Set()) {
|
|
|
22764
23045
|
const lines = [];
|
|
22765
23046
|
for (const rawField of fields) {
|
|
22766
23047
|
const field = promoteFieldType$1(rawField);
|
|
22767
|
-
const kotlinName =
|
|
23048
|
+
const kotlinName = domainPropertyName(field);
|
|
22768
23049
|
if (seen.has(kotlinName)) continue;
|
|
22769
23050
|
seen.add(kotlinName);
|
|
22770
23051
|
const baseType = mapTypeRef$2(field.type);
|
|
@@ -24618,7 +24899,7 @@ function buildResponseAssertions(responseModelName, ctx) {
|
|
|
24618
24899
|
for (const field of model.fields) {
|
|
24619
24900
|
if (!field.required) continue;
|
|
24620
24901
|
if (assertions.length >= MAX_RESPONSE_ASSERTIONS) break;
|
|
24621
|
-
const ktProp =
|
|
24902
|
+
const ktProp = domainPropertyName(field);
|
|
24622
24903
|
const type = field.type;
|
|
24623
24904
|
if (type.kind === "primitive") {
|
|
24624
24905
|
if (type.format === "date-time") continue;
|
|
@@ -25062,7 +25343,7 @@ const kotlinEmitter = {
|
|
|
25062
25343
|
return buildOperationsMap$2(spec, ctx);
|
|
25063
25344
|
},
|
|
25064
25345
|
fileHeader() {
|
|
25065
|
-
return
|
|
25346
|
+
return `// ${AUTOGEN_NOTICE}`;
|
|
25066
25347
|
},
|
|
25067
25348
|
formatCommand(targetDir) {
|
|
25068
25349
|
if (!fs.existsSync(path.join(targetDir, "gradlew"))) return null;
|
|
@@ -25114,6 +25395,15 @@ function fieldName$1(name) {
|
|
|
25114
25395
|
return toSnakeCase(name);
|
|
25115
25396
|
}
|
|
25116
25397
|
/**
|
|
25398
|
+
* snake_case domain field name for a model field, honoring a `domainName`
|
|
25399
|
+
* override (set via the `fieldHints` config) so a wire field can surface under
|
|
25400
|
+
* a friendlier name. The wire key (still derived from `field.name`) is what
|
|
25401
|
+
* gets sent/received over the wire — only the domain attr/accessor name changes.
|
|
25402
|
+
*/
|
|
25403
|
+
function domainFieldName$1(field) {
|
|
25404
|
+
return toSnakeCase(field.domainName ?? field.name);
|
|
25405
|
+
}
|
|
25406
|
+
/**
|
|
25117
25407
|
* Ruby reserved words that cannot be used as parameter names.
|
|
25118
25408
|
* When a path/query param name collides, suffix with underscore.
|
|
25119
25409
|
*/
|
|
@@ -25344,7 +25634,7 @@ function generateModels$1(models, ctx) {
|
|
|
25344
25634
|
}
|
|
25345
25635
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
25346
25636
|
const fields = model.fields.filter((f) => {
|
|
25347
|
-
const n =
|
|
25637
|
+
const n = domainFieldName$1(f);
|
|
25348
25638
|
if (seenFieldNames.has(n)) return false;
|
|
25349
25639
|
seenFieldNames.add(n);
|
|
25350
25640
|
return true;
|
|
@@ -25358,7 +25648,7 @@ function generateModels$1(models, ctx) {
|
|
|
25358
25648
|
lines.push(" HASH_ATTRS = {");
|
|
25359
25649
|
for (let i = 0; i < fields.length; i++) {
|
|
25360
25650
|
const field = fields[i];
|
|
25361
|
-
const fname =
|
|
25651
|
+
const fname = domainFieldName$1(field);
|
|
25362
25652
|
const sep = i === fields.length - 1 ? "" : ",";
|
|
25363
25653
|
lines.push(` ${rubyHashLiteralKey(field.name)} :${fname}${sep}`);
|
|
25364
25654
|
}
|
|
@@ -25367,13 +25657,13 @@ function generateModels$1(models, ctx) {
|
|
|
25367
25657
|
if (deprecatedFields.length > 0) {
|
|
25368
25658
|
for (const f of deprecatedFields) {
|
|
25369
25659
|
const desc = f.description ? ` ${f.description.split("\n")[0].trim()}` : "";
|
|
25370
|
-
lines.push(` # @!attribute ${
|
|
25660
|
+
lines.push(` # @!attribute ${domainFieldName$1(f)}`);
|
|
25371
25661
|
lines.push(` # @deprecated${desc}`);
|
|
25372
25662
|
}
|
|
25373
25663
|
lines.push("");
|
|
25374
25664
|
}
|
|
25375
25665
|
if (accessorFields.length > 0) {
|
|
25376
|
-
const attrs = accessorFields.map((f) => `:${
|
|
25666
|
+
const attrs = accessorFields.map((f) => `:${domainFieldName$1(f)}`);
|
|
25377
25667
|
if (attrs.length === 1) lines.push(` attr_accessor ${attrs[0]}`);
|
|
25378
25668
|
else {
|
|
25379
25669
|
lines.push(` attr_accessor \\`);
|
|
@@ -25385,7 +25675,7 @@ function generateModels$1(models, ctx) {
|
|
|
25385
25675
|
lines.push("");
|
|
25386
25676
|
}
|
|
25387
25677
|
for (const f of deprecatedFields) {
|
|
25388
|
-
const fname =
|
|
25678
|
+
const fname = domainFieldName$1(f);
|
|
25389
25679
|
lines.push(` def ${fname}`);
|
|
25390
25680
|
lines.push(` warn "[DEPRECATION] \\\`${fname}\\\` is deprecated and will be removed in a future version.", uplevel: 1`);
|
|
25391
25681
|
lines.push(` @${fname}`);
|
|
@@ -25399,7 +25689,7 @@ function generateModels$1(models, ctx) {
|
|
|
25399
25689
|
lines.push(" def initialize(json)");
|
|
25400
25690
|
lines.push(" hash = self.class.normalize(json)");
|
|
25401
25691
|
for (const field of fields) {
|
|
25402
|
-
const fname =
|
|
25692
|
+
const fname = domainFieldName$1(field);
|
|
25403
25693
|
const rawKey = field.name;
|
|
25404
25694
|
lines.push(` ${deserializeAssignment(fname, rawKey, field.type, field.required, enumNames, modelNames)}`);
|
|
25405
25695
|
}
|
|
@@ -26895,7 +27185,7 @@ function generateModelRoundTripTest(spec) {
|
|
|
26895
27185
|
const dedupFields = /* @__PURE__ */ new Set();
|
|
26896
27186
|
for (const f of model.fields) {
|
|
26897
27187
|
const wireName = f.name;
|
|
26898
|
-
const rubyFieldName =
|
|
27188
|
+
const rubyFieldName = domainFieldName$1(f);
|
|
26899
27189
|
if (dedupFields.has(rubyFieldName)) continue;
|
|
26900
27190
|
dedupFields.add(rubyFieldName);
|
|
26901
27191
|
const stub = roundTripStub(f.type, enumNames);
|
|
@@ -27224,7 +27514,7 @@ function generateRbiFiles(spec, ctx) {
|
|
|
27224
27514
|
lines.push("");
|
|
27225
27515
|
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
27226
27516
|
for (const f of model.fields) {
|
|
27227
|
-
const fname =
|
|
27517
|
+
const fname = domainFieldName$1(f);
|
|
27228
27518
|
if (seenFieldNames.has(fname)) continue;
|
|
27229
27519
|
seenFieldNames.add(fname);
|
|
27230
27520
|
const sorbetType = f.required ? mapSorbetType(f.type) : wrapNilable(mapSorbetType(f.type));
|
|
@@ -27501,7 +27791,7 @@ const rubyEmitter = {
|
|
|
27501
27791
|
return buildOperationsMap$1(spec, ctx);
|
|
27502
27792
|
},
|
|
27503
27793
|
fileHeader() {
|
|
27504
|
-
return `# frozen_string_literal: true\n\n#
|
|
27794
|
+
return `# frozen_string_literal: true\n\n# ${AUTOGEN_NOTICE}`;
|
|
27505
27795
|
},
|
|
27506
27796
|
formatCommand(targetDir) {
|
|
27507
27797
|
return {
|
|
@@ -27587,6 +27877,15 @@ function methodName(name) {
|
|
|
27587
27877
|
function fieldName(name) {
|
|
27588
27878
|
return escapeKeyword(toSnakeCase(name));
|
|
27589
27879
|
}
|
|
27880
|
+
/**
|
|
27881
|
+
* snake_case domain field name for a model field, honoring a `domainName`
|
|
27882
|
+
* override (set via the `fieldHints` config) so a wire field can surface under
|
|
27883
|
+
* a friendlier identifier. The wire name (and thus the `#[serde(rename = ...)]`
|
|
27884
|
+
* key) still derives from `field.name`.
|
|
27885
|
+
*/
|
|
27886
|
+
function domainFieldName(field) {
|
|
27887
|
+
return escapeKeyword(toSnakeCase(field.domainName ?? field.name));
|
|
27888
|
+
}
|
|
27590
27889
|
/** PascalCase enum variant. */
|
|
27591
27890
|
function variantName(value) {
|
|
27592
27891
|
const pascal = toPascalCase(String(value));
|
|
@@ -27858,6 +28157,7 @@ function generateModels(models, ctx, registry) {
|
|
|
27858
28157
|
const files = [];
|
|
27859
28158
|
const moduleNames = [];
|
|
27860
28159
|
const seen = /* @__PURE__ */ new Set();
|
|
28160
|
+
const taggedVariantFields = collectTaggedVariantFields(models);
|
|
27861
28161
|
for (const model of models) {
|
|
27862
28162
|
const mod = moduleName(model.name);
|
|
27863
28163
|
if (seen.has(mod)) continue;
|
|
@@ -27866,7 +28166,7 @@ function generateModels(models, ctx, registry) {
|
|
|
27866
28166
|
const path = ctx.overlayLookup?.fileBySymbol?.get(model.name) ?? `src/models/${mod}.rs`;
|
|
27867
28167
|
files.push({
|
|
27868
28168
|
path,
|
|
27869
|
-
content: renderModel(model, registry),
|
|
28169
|
+
content: renderModel(model, registry, taggedVariantFields.get(model.name)),
|
|
27870
28170
|
overwriteExisting: true
|
|
27871
28171
|
});
|
|
27872
28172
|
}
|
|
@@ -27878,7 +28178,43 @@ function generateModels(models, ctx, registry) {
|
|
|
27878
28178
|
});
|
|
27879
28179
|
return files;
|
|
27880
28180
|
}
|
|
27881
|
-
|
|
28181
|
+
/**
|
|
28182
|
+
* Walk every model field and record which models are arms of an
|
|
28183
|
+
* internally-tagged union, mapped to that union's discriminator property.
|
|
28184
|
+
*/
|
|
28185
|
+
function collectTaggedVariantFields(models) {
|
|
28186
|
+
const out = /* @__PURE__ */ new Map();
|
|
28187
|
+
const visit = (ref) => {
|
|
28188
|
+
switch (ref.kind) {
|
|
28189
|
+
case "union":
|
|
28190
|
+
if (ref.discriminator?.property) for (const variant of ref.variants) {
|
|
28191
|
+
const name = variantModelName(variant);
|
|
28192
|
+
if (name) out.set(name, ref.discriminator.property);
|
|
28193
|
+
}
|
|
28194
|
+
ref.variants.forEach(visit);
|
|
28195
|
+
break;
|
|
28196
|
+
case "array":
|
|
28197
|
+
visit(ref.items);
|
|
28198
|
+
break;
|
|
28199
|
+
case "nullable":
|
|
28200
|
+
visit(ref.inner);
|
|
28201
|
+
break;
|
|
28202
|
+
case "map":
|
|
28203
|
+
visit(ref.valueType);
|
|
28204
|
+
break;
|
|
28205
|
+
default: break;
|
|
28206
|
+
}
|
|
28207
|
+
};
|
|
28208
|
+
for (const model of models) for (const field of model.fields) visit(field.type);
|
|
28209
|
+
return out;
|
|
28210
|
+
}
|
|
28211
|
+
/** Resolve the underlying model name of a union arm, unwrapping a nullable. */
|
|
28212
|
+
function variantModelName(ref) {
|
|
28213
|
+
if (ref.kind === "model") return ref.name;
|
|
28214
|
+
if (ref.kind === "nullable") return variantModelName(ref.inner);
|
|
28215
|
+
return null;
|
|
28216
|
+
}
|
|
28217
|
+
function renderModel(model, registry, tagField) {
|
|
27882
28218
|
const lines = [];
|
|
27883
28219
|
lines.push(HEADER_PLACEHOLDER);
|
|
27884
28220
|
lines.push("#[allow(unused_imports)]");
|
|
@@ -27890,7 +28226,7 @@ function renderModel(model, registry) {
|
|
|
27890
28226
|
if (model.description) lines.push(...docComment$1(model.description));
|
|
27891
28227
|
lines.push("#[derive(Debug, Clone, Serialize, Deserialize)]");
|
|
27892
28228
|
const resolvedNames = resolveFieldNames(model.fields);
|
|
27893
|
-
const fieldLines = model.fields.map((f, i) => renderField(f, resolvedNames[i], model.name, registry));
|
|
28229
|
+
const fieldLines = model.fields.map((f, i) => renderField(f, resolvedNames[i], model.name, registry, tagField));
|
|
27894
28230
|
if (fieldLines.length === 0) lines.push(`pub struct ${typeName(model.name)} {}`);
|
|
27895
28231
|
else {
|
|
27896
28232
|
lines.push(`pub struct ${typeName(model.name)} {`);
|
|
@@ -27900,17 +28236,20 @@ function renderModel(model, registry) {
|
|
|
27900
28236
|
return lines.filter((l) => l !== HEADER_PLACEHOLDER).join("\n") + "\n";
|
|
27901
28237
|
}
|
|
27902
28238
|
/**
|
|
27903
|
-
* Resolve unique Rust identifiers for struct fields.
|
|
27904
|
-
*
|
|
27905
|
-
* `
|
|
27906
|
-
*
|
|
27907
|
-
*
|
|
28239
|
+
* Resolve unique Rust identifiers for struct fields. The domain identifier
|
|
28240
|
+
* honors a `fieldHints` override (`domainName`, e.g. wire `connection_type` →
|
|
28241
|
+
* domain `type`); the wire name (and the `#[serde(rename = ...)]` key emitted
|
|
28242
|
+
* in `renderField`) still derives from `f.name`. Multiple names can collide
|
|
28243
|
+
* after snake-casing (e.g. `integration_type` and `integrationType` both
|
|
28244
|
+
* become `integration_type`). Subsequent collisions get a numeric suffix so
|
|
28245
|
+
* the struct compiles; serde `rename` preserves the original wire name in every
|
|
28246
|
+
* case.
|
|
27908
28247
|
*/
|
|
27909
28248
|
function resolveFieldNames(fields) {
|
|
27910
28249
|
const used = /* @__PURE__ */ new Set();
|
|
27911
28250
|
const out = [];
|
|
27912
28251
|
for (const f of fields) {
|
|
27913
|
-
const base =
|
|
28252
|
+
const base = domainFieldName(f);
|
|
27914
28253
|
let candidate = base;
|
|
27915
28254
|
let suffix = 2;
|
|
27916
28255
|
while (used.has(candidate)) {
|
|
@@ -27922,7 +28261,7 @@ function resolveFieldNames(fields) {
|
|
|
27922
28261
|
}
|
|
27923
28262
|
return out;
|
|
27924
28263
|
}
|
|
27925
|
-
function renderField(field, rustField, modelName, registry) {
|
|
28264
|
+
function renderField(field, rustField, modelName, registry, tagField) {
|
|
27926
28265
|
const lines = [];
|
|
27927
28266
|
const hasDescription = !!field.description;
|
|
27928
28267
|
if (hasDescription) for (const c of docComment$1(field.description)) lines.push(` ${c}`);
|
|
@@ -27937,8 +28276,13 @@ function renderField(field, rustField, modelName, registry) {
|
|
|
27937
28276
|
});
|
|
27938
28277
|
if ((!field.required || field.type.kind === "nullable") && !baseType.startsWith("Option<")) baseType = makeOptional(baseType);
|
|
27939
28278
|
baseType = applySecretRedaction(baseType, field.name);
|
|
27940
|
-
if (
|
|
27941
|
-
|
|
28279
|
+
if (tagField === field.name) {
|
|
28280
|
+
const args = rename ? `rename = "${rename}", default, skip_serializing` : "default, skip_serializing";
|
|
28281
|
+
lines.push(` #[serde(${args})]`);
|
|
28282
|
+
} else {
|
|
28283
|
+
if (rename) lines.push(` #[serde(rename = "${rename}")]`);
|
|
28284
|
+
if (baseType.startsWith("Option<")) lines.push(" #[serde(skip_serializing_if = \"Option::is_none\", default)]");
|
|
28285
|
+
}
|
|
27942
28286
|
if (field.deprecated) lines.push(" #[deprecated]");
|
|
27943
28287
|
lines.push(` pub ${rustField}: ${baseType},`);
|
|
27944
28288
|
return lines.join("\n");
|
|
@@ -28520,7 +28864,7 @@ function registerSyntheticBody(op, paramsName, bodyGroupParamNames, bodyGroupFie
|
|
|
28520
28864
|
if (!f.required && !rust.startsWith("Option<")) rust = makeOptional(rust);
|
|
28521
28865
|
rust = applySecretRedaction(rust, f.name);
|
|
28522
28866
|
return {
|
|
28523
|
-
rustName:
|
|
28867
|
+
rustName: domainFieldName(f),
|
|
28524
28868
|
wireName: f.name,
|
|
28525
28869
|
rustType: rust,
|
|
28526
28870
|
required: !!f.required && !rust.startsWith("Option<"),
|
|
@@ -29875,7 +30219,7 @@ const rustEmitter = {
|
|
|
29875
30219
|
return buildOperationsMap(spec, ctx);
|
|
29876
30220
|
},
|
|
29877
30221
|
fileHeader() {
|
|
29878
|
-
return
|
|
30222
|
+
return `// ${AUTOGEN_NOTICE}`;
|
|
29879
30223
|
},
|
|
29880
30224
|
formatCommand(_targetDir) {
|
|
29881
30225
|
return {
|
|
@@ -29930,4 +30274,4 @@ const workosEmittersPlugin = {
|
|
|
29930
30274
|
//#endregion
|
|
29931
30275
|
export { fieldName$2 as A, servicePropertyName$2 as B, apiClassName as C, dotnetEmitter as D, propertyName as E, fieldName$3 as F, fieldName$5 as H, methodName$3 as I, trimMountedResourceFromMethod$2 as L, trimMountedResourceFromMethod$1 as M, goEmitter as N, appendAsyncSuffix as O, className$3 as P, phpEmitter as R, kotlinEmitter as S, packageSegment as T, safeParamName$1 as U, pythonEmitter as V, nodeEmitter as W, rubyEmitter as _, rustExtractor as a, resolveServiceTarget as b, pythonExtractor as c, rustEmitter as d, fieldName as f, typeName as g, resourceAccessorName as h, kotlinExtractor as i, methodName$2 as j, className$2 as k, rubyExtractor as l, moduleName as m, elixirExtractor as n, goExtractor as o, methodName as p, dotnetExtractor as r, phpExtractor as s, workosEmittersPlugin as t, nodeExtractor as u, buildExportedClassNameSet as v, methodName$1 as w, safeParamName as x, fieldName$1 as y, fieldName$4 as z };
|
|
29932
30276
|
|
|
29933
|
-
//# sourceMappingURL=plugin-
|
|
30277
|
+
//# sourceMappingURL=plugin-Cciic50q.mjs.map
|