@workos/oagen-emitters 0.14.4 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +19 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{plugin-BGVaMGqe.mjs → plugin-C2Hp2Vs2.mjs} +1039 -274
- package/dist/plugin-C2Hp2Vs2.mjs.map +1 -0
- package/dist/plugin.mjs +1 -1
- package/package.json +7 -7
- package/renovate.json +1 -61
- package/src/go/client.ts +1 -1
- package/src/go/enums.ts +77 -0
- package/src/kotlin/enums.ts +11 -4
- package/src/node/client.ts +158 -2
- package/src/node/discriminated-models.ts +68 -24
- package/src/node/field-plan.ts +64 -8
- package/src/node/index.ts +59 -3
- package/src/node/models.ts +73 -30
- package/src/node/naming.ts +14 -1
- package/src/node/node-overrides.ts +4 -37
- package/src/node/options.ts +29 -1
- package/src/node/resources.ts +553 -89
- package/src/node/tests.ts +108 -7
- package/src/php/fixtures.ts +4 -1
- package/src/php/models.ts +3 -1
- package/src/php/resources.ts +40 -11
- package/src/php/tests.ts +22 -12
- package/src/python/client.ts +0 -8
- package/src/python/enums.ts +41 -15
- package/src/python/fixtures.ts +23 -7
- package/src/python/models.ts +26 -5
- package/src/python/resources.ts +71 -3
- package/src/python/tests.ts +70 -12
- package/src/python/wrappers.ts +25 -4
- package/src/ruby/client.ts +0 -1
- package/src/rust/resources.ts +10 -7
- package/src/shared/non-spec-services.ts +0 -5
- package/test/go/enums.test.ts +24 -0
- package/test/node/resources.test.ts +11 -1
- package/test/node/tests.test.ts +3 -3
- package/test/php/client.test.ts +0 -1
- package/test/php/resources.test.ts +50 -0
- package/test/rust/resources.test.ts +9 -0
- package/dist/plugin-BGVaMGqe.mjs.map +0 -1
|
@@ -2855,6 +2855,15 @@ function isAdoptedModelName(name) {
|
|
|
2855
2855
|
return adoptedModelNames.has(name);
|
|
2856
2856
|
}
|
|
2857
2857
|
/**
|
|
2858
|
+
* IR model names handled by the discriminated-models module. These must not
|
|
2859
|
+
* be remapped by the structural matcher because that module emits files and
|
|
2860
|
+
* helpers under the original IR names.
|
|
2861
|
+
*/
|
|
2862
|
+
let discriminatedModelNames = /* @__PURE__ */ new Set();
|
|
2863
|
+
function setDiscriminatedModelNames(names) {
|
|
2864
|
+
discriminatedModelNames = names;
|
|
2865
|
+
}
|
|
2866
|
+
/**
|
|
2858
2867
|
* Domain names that `resolveInterfaceName` reached via a structural rename
|
|
2859
2868
|
* — the resolved name differs from the IR model's own name. `wireInterfaceName`
|
|
2860
2869
|
* consults this set to decide whether to fire the "single-form wire" case:
|
|
@@ -2980,7 +2989,7 @@ function resolveClassName$5(service, ctx) {
|
|
|
2980
2989
|
function resolveInterfaceName(name, ctx, opts) {
|
|
2981
2990
|
const existing = ctx.overlayLookup?.interfaceByName?.get(name);
|
|
2982
2991
|
if (existing) return existing;
|
|
2983
|
-
let inferred = adoptedModelNames.has(name) ? void 0 : ctx.overlayLookup?.modelNameByIR?.get(name);
|
|
2992
|
+
let inferred = adoptedModelNames.has(name) || discriminatedModelNames.has(name) ? void 0 : ctx.overlayLookup?.modelNameByIR?.get(name);
|
|
2984
2993
|
if (inferred) {
|
|
2985
2994
|
if (inferred.startsWith("Serialized")) {
|
|
2986
2995
|
const stripped = inferred.slice(10);
|
|
@@ -3825,6 +3834,25 @@ function assignEnumsToServices(enums, services, models = [], ctx) {
|
|
|
3825
3834
|
return enumToService;
|
|
3826
3835
|
}
|
|
3827
3836
|
//#endregion
|
|
3837
|
+
//#region src/node/options.ts
|
|
3838
|
+
function nodeOptions(ctx) {
|
|
3839
|
+
return ctx.emitterOptions ?? {};
|
|
3840
|
+
}
|
|
3841
|
+
function normalizeServiceName(name) {
|
|
3842
|
+
return name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
|
3843
|
+
}
|
|
3844
|
+
function ownedLookupNames(name) {
|
|
3845
|
+
const names = [name];
|
|
3846
|
+
if (name.startsWith("__baseline_dir__:")) names.push(name.slice(17));
|
|
3847
|
+
return names;
|
|
3848
|
+
}
|
|
3849
|
+
function isNodeOwnedService(ctx, ...names) {
|
|
3850
|
+
const configured = nodeOptions(ctx).ownedServices ?? [];
|
|
3851
|
+
if (configured.length === 0) return false;
|
|
3852
|
+
const owned = new Set(configured.map(normalizeServiceName));
|
|
3853
|
+
return names.some((name) => name !== void 0 ? ownedLookupNames(name).some((candidate) => owned.has(normalizeServiceName(candidate))) : false);
|
|
3854
|
+
}
|
|
3855
|
+
//#endregion
|
|
3828
3856
|
//#region src/node/field-plan.ts
|
|
3829
3857
|
/**
|
|
3830
3858
|
* Decide whether a `deserialize${X}` / `serialize${X}` helper will be
|
|
@@ -3843,6 +3871,15 @@ function helperExists(helperName, depModelName, ctx) {
|
|
|
3843
3871
|
if (liveSurfaceHasFunction(helperName)) return true;
|
|
3844
3872
|
const depModel = ctx.spec.models.find((m) => m.name === depModelName);
|
|
3845
3873
|
if (!depModel) return false;
|
|
3874
|
+
const depService = assignModelsToServices$1(ctx.spec.models, ctx.spec.services, ctx.modelHints).get(depModelName);
|
|
3875
|
+
const resolvedName = resolveInterfaceName(depModelName, ctx);
|
|
3876
|
+
const siblingPath = liveSurfaceFunctionPath(`${helperName.startsWith("serialize") ? "deserialize" : "serialize"}${resolvedName}`);
|
|
3877
|
+
if (siblingPath && liveSurfaceHasFile(siblingPath) && !liveSurfaceHasAutogenFile(siblingPath)) return false;
|
|
3878
|
+
const sourceFile = (ctx.apiSurface?.interfaces?.[resolvedName])?.sourceFile;
|
|
3879
|
+
const { resolveDir } = createServiceDirResolver(ctx.spec.models, ctx.spec.services, ctx);
|
|
3880
|
+
const candidate = sourceFile ? sourceFile.replace("/interfaces/", "/serializers/").replace(".interface.ts", ".serializer.ts") : `src/${resolveDir(depService)}/serializers/${fileName$3(depModelName)}.serializer.ts`;
|
|
3881
|
+
if (liveSurfaceHasFile(candidate) && !liveSurfaceHasAutogenFile(candidate)) return false;
|
|
3882
|
+
if (isNodeOwnedService(ctx, depService)) return true;
|
|
3846
3883
|
return modelHasNewFields(depModel, ctx);
|
|
3847
3884
|
}
|
|
3848
3885
|
function deserializeExpression$1(ref, wireExpr, ctx, nullFallback = "null") {
|
|
@@ -4201,11 +4238,12 @@ function buildSerializerImports(model, serializerPath, dirName, domainName, resp
|
|
|
4201
4238
|
const depService = sctx.modelToService.get(dep);
|
|
4202
4239
|
const depDir = sctx.resolveDir(depService);
|
|
4203
4240
|
const depName = resolveInterfaceName(dep, sctx.ctx);
|
|
4204
|
-
const
|
|
4241
|
+
const depIsOwned = isNodeOwnedService(sctx.ctx, depService);
|
|
4242
|
+
const baselineSrc = depIsOwned ? void 0 : (sctx.ctx.apiSurface?.interfaces?.[depName])?.sourceFile;
|
|
4205
4243
|
const baselineSerializerPath = baselineSrc ? baselineSrc.replace("/interfaces/", "/serializers/").replace(".interface.ts", ".serializer.ts") : null;
|
|
4206
4244
|
const irNameSerializerPath = `src/${depDir}/serializers/${fileName$3(dep)}.serializer.ts`;
|
|
4207
|
-
const liveDeserPath = liveSurfaceFunctionPath(`deserialize${depName}`);
|
|
4208
|
-
const liveSerPath = liveSurfaceFunctionPath(`serialize${depName}`);
|
|
4245
|
+
const liveDeserPath = depIsOwned ? void 0 : liveSurfaceFunctionPath(`deserialize${depName}`);
|
|
4246
|
+
const liveSerPath = depIsOwned ? void 0 : liveSurfaceFunctionPath(`serialize${depName}`);
|
|
4209
4247
|
const depSerializerPath = liveDeserPath ?? liveSerPath ?? (baselineSerializerPath && liveSurfaceHasFile(baselineSerializerPath) ? baselineSerializerPath : irNameSerializerPath);
|
|
4210
4248
|
const rel = relativeImport(serializerPath, depSerializerPath);
|
|
4211
4249
|
const canon = sctx.dedup.get(dep);
|
|
@@ -4213,11 +4251,11 @@ function buildSerializerImports(model, serializerPath, dirName, domainName, resp
|
|
|
4213
4251
|
const depSkipDeserialize = sctx.responseReachableModels !== void 0 && !sctx.responseReachableModels.has(dep) && (canon == null || !sctx.responseReachableModels.has(canon));
|
|
4214
4252
|
const hasDeser = liveSurfaceHasFunction(`deserialize${depName}`);
|
|
4215
4253
|
const hasSer = liveSurfaceHasFunction(`serialize${depName}`);
|
|
4216
|
-
const fileExists = liveSurfaceHasFile(depSerializerPath);
|
|
4254
|
+
const fileExists = !depIsOwned && liveSurfaceHasFile(depSerializerPath);
|
|
4217
4255
|
if (fileExists && !hasDeser && !hasSer) continue;
|
|
4218
4256
|
if (!fileExists) {
|
|
4219
4257
|
const depModel = sctx.ctx.spec.models.find((m) => m.name === dep);
|
|
4220
|
-
if (!(depModel ? modelHasNewFields(depModel, sctx.ctx) : true)) continue;
|
|
4258
|
+
if (!(depModel ? depIsOwned || modelHasNewFields(depModel, sctx.ctx) : true)) continue;
|
|
4221
4259
|
}
|
|
4222
4260
|
if (fileExists && depSkipSerialize) {
|
|
4223
4261
|
if (hasDeser) lines.push(`import { deserialize${depName} } from '${rel}';`);
|
|
@@ -4252,7 +4290,7 @@ function buildSkipFormatFields(model, baselineDomain) {
|
|
|
4252
4290
|
return skipFormatFields;
|
|
4253
4291
|
}
|
|
4254
4292
|
function shouldSkipSerializeForModel(model, baselineResponse, baselineDomain, dedup, skippedSerializeModels, ctx) {
|
|
4255
|
-
let shouldSkip = serializerHasBaselineIncompatibility(model, baselineResponse, baselineDomain, ctx);
|
|
4293
|
+
let shouldSkip = serializerHasBaselineIncompatibility(model, baselineResponse, baselineDomain, ctx) || hasUnsafeSerializePassthrough(model, baselineDomain, baselineResponse, ctx);
|
|
4256
4294
|
if (!shouldSkip) for (const field of model.fields) {
|
|
4257
4295
|
for (const ref of collectSerializedModelRefs(field.type)) {
|
|
4258
4296
|
if (skippedSerializeModels.has(ref)) {
|
|
@@ -4264,6 +4302,11 @@ function shouldSkipSerializeForModel(model, baselineResponse, baselineDomain, de
|
|
|
4264
4302
|
shouldSkip = true;
|
|
4265
4303
|
break;
|
|
4266
4304
|
}
|
|
4305
|
+
const resolved = resolveInterfaceName(ref, ctx);
|
|
4306
|
+
if (wireInterfaceName(resolved) !== resolved && !helperExists(`serialize${resolved}`, ref, ctx)) {
|
|
4307
|
+
shouldSkip = true;
|
|
4308
|
+
break;
|
|
4309
|
+
}
|
|
4267
4310
|
}
|
|
4268
4311
|
if (shouldSkip) break;
|
|
4269
4312
|
}
|
|
@@ -4271,6 +4314,7 @@ function shouldSkipSerializeForModel(model, baselineResponse, baselineDomain, de
|
|
|
4271
4314
|
}
|
|
4272
4315
|
function emitSerializerBody(model, domainName, responseName, typeParams, baselineDomain, baselineResponse, skipFormatFields, shouldSkipSerialize, shouldSkipDeserialize, ctx) {
|
|
4273
4316
|
const lines = [];
|
|
4317
|
+
const effectiveShouldSkipSerialize = shouldSkipSerialize || hasUnsafeSerializePassthrough(model, baselineDomain, baselineResponse, ctx);
|
|
4274
4318
|
if (!shouldSkipDeserialize) {
|
|
4275
4319
|
const seenDeserFields = /* @__PURE__ */ new Set();
|
|
4276
4320
|
const deserParamPrefix = model.fields.length === 0 ? "_" : "";
|
|
@@ -4286,7 +4330,7 @@ function emitSerializerBody(model, domainName, responseName, typeParams, baselin
|
|
|
4286
4330
|
}
|
|
4287
4331
|
lines.push("});");
|
|
4288
4332
|
}
|
|
4289
|
-
if (!
|
|
4333
|
+
if (!effectiveShouldSkipSerialize) {
|
|
4290
4334
|
if (!shouldSkipDeserialize) lines.push("");
|
|
4291
4335
|
const serParamPrefix = model.fields.length === 0 ? "_" : "";
|
|
4292
4336
|
lines.push(`export const serialize${domainName} = ${typeParams.decl}(`);
|
|
@@ -4304,19 +4348,18 @@ function emitSerializerBody(model, domainName, responseName, typeParams, baselin
|
|
|
4304
4348
|
}
|
|
4305
4349
|
return lines;
|
|
4306
4350
|
}
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
return names.some((name) => name !== void 0 && owned.has(normalizeServiceName(name)));
|
|
4351
|
+
function hasUnsafeSerializePassthrough(model, baselineDomain, baselineResponse, ctx) {
|
|
4352
|
+
if (!baselineDomain?.fields || !baselineResponse?.fields) return false;
|
|
4353
|
+
for (const field of model.fields) {
|
|
4354
|
+
const domain = fieldName$6(field.name);
|
|
4355
|
+
const wire = wireFieldName(field.name);
|
|
4356
|
+
const domainField = baselineDomain.fields[domain];
|
|
4357
|
+
const wireField = baselineResponse.fields[wire];
|
|
4358
|
+
if (!domainField || !wireField || domainField.type === wireField.type) continue;
|
|
4359
|
+
const domainAccess = `model.${domain}`;
|
|
4360
|
+
if (serializeExpression(field.type, domainAccess, ctx) === domainAccess) return true;
|
|
4361
|
+
}
|
|
4362
|
+
return false;
|
|
4320
4363
|
}
|
|
4321
4364
|
//#endregion
|
|
4322
4365
|
//#region src/node/sdk-errors.ts
|
|
@@ -4634,10 +4677,52 @@ function paginatedOptionsName(method, resolvedServiceName) {
|
|
|
4634
4677
|
if (method === "list") return `${toPascalCase(resolvedServiceName)}ListOptions`;
|
|
4635
4678
|
return toPascalCase(method) + "Options";
|
|
4636
4679
|
}
|
|
4680
|
+
function recordGeneratedOptionInterface(ctx, serviceDir, stem, typeName) {
|
|
4681
|
+
const key = "_nodeGeneratedOptionInterfaceExports";
|
|
4682
|
+
const registry = ctx[key] ??= /* @__PURE__ */ new Map();
|
|
4683
|
+
const exports = registry.get(serviceDir) ?? [];
|
|
4684
|
+
if (!exports.some((entry) => entry.stem === stem && entry.typeName === typeName)) exports.push({
|
|
4685
|
+
stem,
|
|
4686
|
+
typeName
|
|
4687
|
+
});
|
|
4688
|
+
registry.set(serviceDir, exports);
|
|
4689
|
+
}
|
|
4690
|
+
function existingInterfaceBarrelExports(ctx, serviceDir, stem) {
|
|
4691
|
+
const root = ctx.outputDir ?? ctx.targetDir;
|
|
4692
|
+
if (!root) return false;
|
|
4693
|
+
const barrelPath = path.join(root, "src", serviceDir, "interfaces", "index.ts");
|
|
4694
|
+
let content;
|
|
4695
|
+
try {
|
|
4696
|
+
content = fs.readFileSync(barrelPath, "utf8");
|
|
4697
|
+
} catch {
|
|
4698
|
+
return false;
|
|
4699
|
+
}
|
|
4700
|
+
const escapedStem = stem.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4701
|
+
return new RegExp(`export\\s+(?:type\\s+)?(?:\\*|\\{[^}]+\\})\\s+from\\s+['"]\\./${escapedStem}['"]`).test(content);
|
|
4702
|
+
}
|
|
4703
|
+
function operationOverrideFor$1(ctx, op) {
|
|
4704
|
+
return nodeOptions(ctx).operationOverrides?.[`${op.httpMethod.toUpperCase()} ${op.path}`];
|
|
4705
|
+
}
|
|
4637
4706
|
function baselineMethodFor$1(service, method, ctx) {
|
|
4638
4707
|
const serviceClass = resolveResourceClassName$3(service, ctx);
|
|
4639
4708
|
return ctx.apiSurface?.classes?.[serviceClass]?.methods?.[method]?.[0];
|
|
4640
4709
|
}
|
|
4710
|
+
function ignoredResourceMethodNames(ctx, resourcePath) {
|
|
4711
|
+
const root = ctx.outputDir ?? ctx.targetDir;
|
|
4712
|
+
if (!root) return /* @__PURE__ */ new Set();
|
|
4713
|
+
let content;
|
|
4714
|
+
try {
|
|
4715
|
+
content = fs.readFileSync(path.join(root, resourcePath), "utf8");
|
|
4716
|
+
} catch {
|
|
4717
|
+
return /* @__PURE__ */ new Set();
|
|
4718
|
+
}
|
|
4719
|
+
const methods = /* @__PURE__ */ new Set();
|
|
4720
|
+
for (const block of content.matchAll(/@oagen-ignore-start[\s\S]*?@oagen-ignore-end/g)) for (const line of block[0].split("\n")) {
|
|
4721
|
+
const match = line.match(/^\s{2}(?:(?:public|private|protected)\s+)?(?:async\s+)?([A-Za-z_$][\w$]*)\s*\(/);
|
|
4722
|
+
if (match) methods.add(match[1]);
|
|
4723
|
+
}
|
|
4724
|
+
return methods;
|
|
4725
|
+
}
|
|
4641
4726
|
function optionsObjectParam$1(method) {
|
|
4642
4727
|
if (!method || method.params.length !== 1) return void 0;
|
|
4643
4728
|
const [param] = method.params;
|
|
@@ -4645,12 +4730,58 @@ function optionsObjectParam$1(method) {
|
|
|
4645
4730
|
if (param.passingStyle && param.passingStyle !== "options_object") return void 0;
|
|
4646
4731
|
if (!param.type || /^(Record|object|any|unknown)\b/.test(param.type)) return void 0;
|
|
4647
4732
|
return {
|
|
4648
|
-
name:
|
|
4649
|
-
type: param.type
|
|
4733
|
+
name: "options",
|
|
4734
|
+
type: param.type,
|
|
4735
|
+
optional: param.optional === true,
|
|
4736
|
+
generated: false
|
|
4650
4737
|
};
|
|
4651
4738
|
}
|
|
4739
|
+
function methodOptionsName$1(method, resolvedServiceName) {
|
|
4740
|
+
if (method === "list") return `${toPascalCase(resolvedServiceName)}ListOptions`;
|
|
4741
|
+
return `${toPascalCase(method)}Options`;
|
|
4742
|
+
}
|
|
4743
|
+
function hiddenParamsFor(resolvedOp) {
|
|
4744
|
+
return new Set([...Object.keys(getOpDefaults(resolvedOp)), ...getOpInferFromClient(resolvedOp)]);
|
|
4745
|
+
}
|
|
4746
|
+
function visibleQueryParamsForOptions(op, plan, resolvedOp) {
|
|
4747
|
+
const hidden = hiddenParamsFor(resolvedOp);
|
|
4748
|
+
return op.queryParams.filter((param) => {
|
|
4749
|
+
if (hidden.has(param.name)) return false;
|
|
4750
|
+
if (plan.isPaginated && PAGINATION_PARAM_NAMES.has(param.name)) return false;
|
|
4751
|
+
return true;
|
|
4752
|
+
});
|
|
4753
|
+
}
|
|
4754
|
+
function optionsObjectShouldBeOptional(op, plan, resolvedOp) {
|
|
4755
|
+
if (plan.hasBody) return false;
|
|
4756
|
+
if (op.pathParams.length > 0) return false;
|
|
4757
|
+
return visibleQueryParamsForOptions(op, plan, resolvedOp).every((param) => !param.required);
|
|
4758
|
+
}
|
|
4759
|
+
function operationHasOptionsInput(op, plan, resolvedOp) {
|
|
4760
|
+
return op.pathParams.length > 0 || plan.hasBody || plan.isPaginated || visibleQueryParamsForOptions(op, plan, resolvedOp).length > 0;
|
|
4761
|
+
}
|
|
4762
|
+
function optionsObjectInfo(service, method, op, plan, ctx, baselineMethod, resolvedOp) {
|
|
4763
|
+
const baseline = optionsObjectParam$1(baselineMethod);
|
|
4764
|
+
if (baseline) return baseline;
|
|
4765
|
+
const overrideType = operationOverrideFor$1(ctx, op)?.optionsType;
|
|
4766
|
+
if (overrideType) return {
|
|
4767
|
+
name: "options",
|
|
4768
|
+
type: overrideType,
|
|
4769
|
+
optional: optionsObjectShouldBeOptional(op, plan, resolvedOp),
|
|
4770
|
+
generated: baselineTypeSourceFile(ctx, overrideType) === void 0
|
|
4771
|
+
};
|
|
4772
|
+
if (!operationHasOptionsInput(op, plan, resolvedOp)) return void 0;
|
|
4773
|
+
return {
|
|
4774
|
+
name: "options",
|
|
4775
|
+
type: methodOptionsName$1(method, resolveResourceClassName$3(service, ctx)),
|
|
4776
|
+
optional: optionsObjectShouldBeOptional(op, plan, resolvedOp),
|
|
4777
|
+
generated: true
|
|
4778
|
+
};
|
|
4779
|
+
}
|
|
4780
|
+
function renderOptionsParam(param) {
|
|
4781
|
+
return `options${param.optional ? "?" : ""}: ${param.type}`;
|
|
4782
|
+
}
|
|
4652
4783
|
function autoPaginatableItemType$1(returnType) {
|
|
4653
|
-
return returnType?.match(/\
|
|
4784
|
+
return returnType?.match(/\b(?:AutoPaginatable|List)<\s*([A-Za-z_$][\w$]*)/)?.[1];
|
|
4654
4785
|
}
|
|
4655
4786
|
function baselineTypeSourceFile(ctx, typeName) {
|
|
4656
4787
|
const surface = ctx.apiSurface;
|
|
@@ -4668,9 +4799,12 @@ function preferredBaselineTypeName(ctx, typeName) {
|
|
|
4668
4799
|
return typeName;
|
|
4669
4800
|
}
|
|
4670
4801
|
function preferredBaselineReturnType(ctx, returnType) {
|
|
4802
|
+
if (!returnType) return void 0;
|
|
4803
|
+
if (!/\bAutoPaginatable\b/.test(returnType)) return void 0;
|
|
4671
4804
|
const itemType = autoPaginatableItemType$1(returnType);
|
|
4805
|
+
if (!itemType) return void 0;
|
|
4672
4806
|
const preferred = preferredBaselineTypeName(ctx, itemType);
|
|
4673
|
-
if (!
|
|
4807
|
+
if (!preferred || preferred === itemType) return returnType;
|
|
4674
4808
|
return returnType.replace(new RegExp(`\\b${itemType}\\b`, "g"), preferred);
|
|
4675
4809
|
}
|
|
4676
4810
|
function requestEntityType(bodyExpr, requestType) {
|
|
@@ -4738,43 +4872,83 @@ function deduplicateMethodNames(plans, _ctx) {
|
|
|
4738
4872
|
}
|
|
4739
4873
|
}
|
|
4740
4874
|
/**
|
|
4741
|
-
* Emit one interface file per
|
|
4742
|
-
*
|
|
4743
|
-
*
|
|
4744
|
-
* is what the root `src/index.ts` re-exports. When the interface was
|
|
4745
|
-
* declared inline in the resource file, it was unreachable from the barrel
|
|
4746
|
-
* and callers couldn't import the type by name from the package root.
|
|
4875
|
+
* Emit one interface/type file per generated options-object operation.
|
|
4876
|
+
* Placing the options type under `interfaces/` lets the per-service barrel
|
|
4877
|
+
* pick it up via `export * from './interfaces'`.
|
|
4747
4878
|
*/
|
|
4748
|
-
function
|
|
4879
|
+
function generateOptionsInterfaces(service, ctx, specEnumNames) {
|
|
4749
4880
|
const files = [];
|
|
4750
|
-
const resolvedName = resolveResourceClassName$3(service, ctx);
|
|
4751
4881
|
const serviceDir = resolveResourceDir(service, ctx);
|
|
4882
|
+
const resolvedLookup = buildResolvedLookup(ctx);
|
|
4752
4883
|
const plans = service.operations.map((op) => ({
|
|
4753
4884
|
op,
|
|
4754
4885
|
plan: planOperation(op),
|
|
4755
4886
|
method: resolveMethodName$6(op, service, ctx)
|
|
4756
4887
|
}));
|
|
4757
4888
|
for (const { op, plan, method } of plans) {
|
|
4758
|
-
|
|
4759
|
-
const
|
|
4760
|
-
if (
|
|
4761
|
-
|
|
4762
|
-
const
|
|
4889
|
+
const resolvedOp = lookupResolved(op, resolvedLookup);
|
|
4890
|
+
const optionInfo = optionsObjectInfo(service, method, op, plan, ctx, baselineMethodFor$1(service, method, ctx), resolvedOp);
|
|
4891
|
+
if (!optionInfo?.generated) continue;
|
|
4892
|
+
if (baselineTypeSourceFile(ctx, optionInfo.type)) continue;
|
|
4893
|
+
const optionsName = optionInfo.type;
|
|
4894
|
+
const optionFileStem = `${fileName$3(optionsName)}.interface`;
|
|
4895
|
+
const filePath = `src/${serviceDir}/interfaces/${optionFileStem}.ts`;
|
|
4896
|
+
if (!liveSurfaceHasFile(filePath) || existingInterfaceBarrelExports(ctx, serviceDir, optionFileStem)) recordGeneratedOptionInterface(ctx, serviceDir, optionFileStem, optionsName);
|
|
4897
|
+
const optEnums = /* @__PURE__ */ new Set();
|
|
4898
|
+
const optModels = /* @__PURE__ */ new Set();
|
|
4899
|
+
for (const param of [...op.pathParams, ...visibleQueryParamsForOptions(op, plan, resolvedOp)]) collectParamTypeRefs(param.type, optEnums, optModels);
|
|
4900
|
+
const bodyInfo = extractRequestBodyType(op, ctx);
|
|
4901
|
+
if (bodyInfo?.kind === "model") {
|
|
4902
|
+
const bodyModel = ctx.spec.models.find((m) => m.name === bodyInfo.name);
|
|
4903
|
+
if (bodyModel) for (const field of bodyModel.fields) collectParamTypeRefs(field.type, optEnums, optModels);
|
|
4904
|
+
} else if (bodyInfo?.kind === "union") for (const name of bodyInfo.modelNames) optModels.add(name);
|
|
4763
4905
|
const lines = [];
|
|
4764
|
-
lines.push("import type { PaginationOptions } from '../../common/interfaces/pagination-options.interface';");
|
|
4906
|
+
if (plan.isPaginated) lines.push("import type { PaginationOptions } from '../../common/interfaces/pagination-options.interface';");
|
|
4907
|
+
const { modelToService, resolveDir } = createServiceDirResolver(ctx.spec.models, ctx.spec.services, ctx);
|
|
4908
|
+
if (optEnums.size > 0) {
|
|
4909
|
+
const enumToService = assignEnumsToServices(ctx.spec.enums, ctx.spec.services, ctx.spec.models, ctx);
|
|
4910
|
+
for (const name of optEnums) {
|
|
4911
|
+
if (!specEnumNames.has(name)) continue;
|
|
4912
|
+
if (isInlineEnum(name)) continue;
|
|
4913
|
+
const enumDir = resolveDir(enumToService.get(name));
|
|
4914
|
+
const relPath = enumDir === serviceDir ? `./${fileName$3(name)}.interface` : `../../${enumDir}/interfaces/${fileName$3(name)}.interface`;
|
|
4915
|
+
lines.push(`import type { ${name} } from '${relPath}';`);
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
for (const name of optModels) {
|
|
4919
|
+
const modelDir = resolveDir(modelToService.get(name));
|
|
4920
|
+
const relPath = modelDir === serviceDir ? `./${fileName$3(name)}.interface` : `../../${modelDir}/interfaces/${fileName$3(name)}.interface`;
|
|
4921
|
+
lines.push(`import type { ${resolveInterfaceName(name, ctx)} } from '${relPath}';`);
|
|
4922
|
+
}
|
|
4765
4923
|
lines.push("");
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
const opt = !
|
|
4769
|
-
if (
|
|
4924
|
+
const headerParts = [];
|
|
4925
|
+
const pushField = (name, required, type, description, deprecated) => {
|
|
4926
|
+
const opt = !required ? "?" : "";
|
|
4927
|
+
if (description || deprecated) {
|
|
4770
4928
|
const parts = [];
|
|
4771
|
-
if (
|
|
4772
|
-
if (
|
|
4773
|
-
|
|
4929
|
+
if (description) parts.push(description);
|
|
4930
|
+
if (deprecated) parts.push("@deprecated");
|
|
4931
|
+
headerParts.push(...docComment$2(parts.join("\n"), 2));
|
|
4774
4932
|
}
|
|
4775
|
-
|
|
4933
|
+
headerParts.push(` ${name}${opt}: ${type};`);
|
|
4934
|
+
};
|
|
4935
|
+
for (const param of op.pathParams) pushField(fieldName$6(param.name), true, mapParamType(param.type, specEnumNames), param.description, param.deprecated);
|
|
4936
|
+
for (const param of visibleQueryParamsForOptions(op, plan, resolvedOp)) pushField(fieldName$6(param.name), param.required, mapParamType(param.type, specEnumNames), param.description, param.deprecated);
|
|
4937
|
+
if (bodyInfo?.kind === "model") {
|
|
4938
|
+
const bodyModel = ctx.spec.models.find((m) => m.name === bodyInfo.name);
|
|
4939
|
+
if (bodyModel) for (const field of bodyModel.fields) pushField(fieldName$6(field.name), field.required, mapParamType(field.type, specEnumNames), field.description, field.deprecated);
|
|
4940
|
+
lines.push(`export interface ${optionsName}${plan.isPaginated ? " extends PaginationOptions" : ""} {`);
|
|
4941
|
+
lines.push(...headerParts);
|
|
4942
|
+
lines.push("}");
|
|
4943
|
+
} else if (bodyInfo?.kind === "union") {
|
|
4944
|
+
const baseType = headerParts.length > 0 ? `{\n${headerParts.join("\n")}\n}` : "{}";
|
|
4945
|
+
lines.push(`export type ${optionsName} = ${baseType} & (${bodyInfo.typeStr});`);
|
|
4946
|
+
} else if (plan.isPaginated && headerParts.length === 0) lines.push(`export type ${optionsName} = PaginationOptions;`);
|
|
4947
|
+
else {
|
|
4948
|
+
lines.push(`export interface ${optionsName}${plan.isPaginated ? " extends PaginationOptions" : ""} {`);
|
|
4949
|
+
lines.push(...headerParts);
|
|
4950
|
+
lines.push("}");
|
|
4776
4951
|
}
|
|
4777
|
-
lines.push("}");
|
|
4778
4952
|
files.push({
|
|
4779
4953
|
path: filePath,
|
|
4780
4954
|
content: lines.join("\n")
|
|
@@ -4819,7 +4993,7 @@ function generateResources$7(services, ctx) {
|
|
|
4819
4993
|
}
|
|
4820
4994
|
for (const service of mergedServices) {
|
|
4821
4995
|
if (!isNodeOwnedService(ctx, service.name, resolveResourceClassName$3(service, ctx)) && isServiceCoveredByExisting(service, ctx) && !hasMethodsAbsentFromBaseline(service, ctx)) continue;
|
|
4822
|
-
files.push(...
|
|
4996
|
+
files.push(...generateOptionsInterfaces(service, ctx, topLevelEnumNames));
|
|
4823
4997
|
}
|
|
4824
4998
|
return files;
|
|
4825
4999
|
}
|
|
@@ -4844,27 +5018,40 @@ function generateResourceClass(service, ctx) {
|
|
|
4844
5018
|
return (httpKeyOrder.get(aKey) ?? Number.MAX_SAFE_INTEGER) - (httpKeyOrder.get(bKey) ?? Number.MAX_SAFE_INTEGER);
|
|
4845
5019
|
});
|
|
4846
5020
|
}
|
|
5021
|
+
const ignoredMethodNames = ignoredResourceMethodNames(ctx, resourcePath);
|
|
4847
5022
|
const baselineMethodNames = new Set(Object.keys(ctx.apiSurface?.classes?.[serviceClass]?.methods ?? {}));
|
|
4848
5023
|
const planCountBeforeFilter = plans.length;
|
|
5024
|
+
if (ignoredMethodNames.size > 0) plans = plans.filter((p) => !ignoredMethodNames.has(p.method));
|
|
4849
5025
|
if (!isNodeOwnedService(ctx, service.name, serviceClass) && baselineMethodNames.size > 0) plans = plans.filter((p) => !baselineMethodNames.has(p.method));
|
|
4850
5026
|
const filteredOut = planCountBeforeFilter - plans.length;
|
|
4851
5027
|
if (plans.length === 0 && filteredOut > 0) return null;
|
|
4852
5028
|
const hasPaginated = plans.some((p) => p.plan.isPaginated);
|
|
4853
|
-
const
|
|
5029
|
+
const resolvedLookup = buildResolvedLookup(ctx);
|
|
5030
|
+
const needsPaginationOptionsImport = plans.some((p) => {
|
|
5031
|
+
const baseline = baselineMethodFor$1(service, p.method, ctx);
|
|
5032
|
+
const optionInfo = optionsObjectInfo(service, p.method, p.op, p.plan, ctx, baseline, lookupResolved(p.op, resolvedLookup));
|
|
5033
|
+
const needsWireSerializer = p.op.queryParams.filter((param) => !PAGINATION_PARAM_NAMES.has(param.name)).some((param) => fieldName$6(param.name) !== wireFieldName(param.name));
|
|
5034
|
+
return p.plan.isPaginated && (needsWireSerializer || !optionInfo || /\bPaginationOptions\b/.test(preferredBaselineReturnType(ctx, baselineMethodFor$1(service, p.method, ctx)?.returnType) ?? ""));
|
|
5035
|
+
});
|
|
4854
5036
|
const modelMap = new Map(ctx.spec.models.map((m) => [m.name, m]));
|
|
4855
5037
|
const responseModels = /* @__PURE__ */ new Set();
|
|
5038
|
+
const responseModelsForSignature = /* @__PURE__ */ new Set();
|
|
4856
5039
|
const requestModels = /* @__PURE__ */ new Set();
|
|
4857
5040
|
const requestModelsForSignature = /* @__PURE__ */ new Set();
|
|
4858
5041
|
const paramEnums = /* @__PURE__ */ new Set();
|
|
4859
5042
|
const paramModels = /* @__PURE__ */ new Set();
|
|
4860
5043
|
const optionObjectTypes = /* @__PURE__ */ new Set();
|
|
5044
|
+
const returnTypeImports = /* @__PURE__ */ new Set();
|
|
4861
5045
|
const baselineResponseTypes = /* @__PURE__ */ new Set();
|
|
4862
|
-
for (const { op, plan } of plans) {
|
|
4863
|
-
const baselineMethod = baselineMethodFor$1(service,
|
|
4864
|
-
const existingOptions =
|
|
5046
|
+
for (const { op, plan, method } of plans) {
|
|
5047
|
+
const baselineMethod = baselineMethodFor$1(service, method, ctx);
|
|
5048
|
+
const existingOptions = optionsObjectInfo(service, method, op, plan, ctx, baselineMethod, lookupResolved(op, resolvedLookup));
|
|
4865
5049
|
if (existingOptions) optionObjectTypes.add(existingOptions.type);
|
|
4866
|
-
const
|
|
4867
|
-
|
|
5050
|
+
for (const typeName of operationOverrideFor$1(ctx, op)?.returnTypeImports ?? []) returnTypeImports.add(typeName);
|
|
5051
|
+
if (!existingOptions) {
|
|
5052
|
+
const queryParams = plan.isPaginated ? [] : op.queryParams;
|
|
5053
|
+
for (const param of [...queryParams, ...op.pathParams]) collectParamTypeRefs(param.type, paramEnums, paramModels);
|
|
5054
|
+
}
|
|
4868
5055
|
const baselinePaginatedItemType = existingOptions ? preferredBaselineTypeName(ctx, autoPaginatableItemType$1(baselineMethod?.returnType)) : void 0;
|
|
4869
5056
|
if (plan.isPaginated && baselinePaginatedItemType) baselineResponseTypes.add(baselinePaginatedItemType);
|
|
4870
5057
|
else if (plan.isPaginated && op.pagination?.itemType.kind === "model") {
|
|
@@ -4875,7 +5062,13 @@ function generateResourceClass(service, ctx) {
|
|
|
4875
5062
|
if (unwrapped) itemName = unwrapped.name;
|
|
4876
5063
|
}
|
|
4877
5064
|
responseModels.add(itemName);
|
|
4878
|
-
|
|
5065
|
+
responseModelsForSignature.add(itemName);
|
|
5066
|
+
} else if (plan.responseModelName) {
|
|
5067
|
+
responseModels.add(plan.responseModelName);
|
|
5068
|
+
const override = operationOverrideFor$1(ctx, op);
|
|
5069
|
+
const resolvedResponseName = resolveInterfaceName(plan.responseModelName, ctx);
|
|
5070
|
+
if (!override?.returnType || override.returnType.includes(resolvedResponseName)) responseModelsForSignature.add(plan.responseModelName);
|
|
5071
|
+
}
|
|
4879
5072
|
const bodyInfo = extractRequestBodyType(op, ctx);
|
|
4880
5073
|
if (bodyInfo?.kind === "model") {
|
|
4881
5074
|
requestModels.add(bodyInfo.name);
|
|
@@ -4889,7 +5082,6 @@ function generateResourceClass(service, ctx) {
|
|
|
4889
5082
|
if (!existingOptions) requestModelsForSignature.add(name);
|
|
4890
5083
|
}
|
|
4891
5084
|
}
|
|
4892
|
-
const resolvedLookup = buildResolvedLookup(ctx);
|
|
4893
5085
|
for (const { op } of plans) {
|
|
4894
5086
|
const resolved = lookupResolved(op, resolvedLookup);
|
|
4895
5087
|
if (resolved) {
|
|
@@ -4898,7 +5090,7 @@ function generateResourceClass(service, ctx) {
|
|
|
4898
5090
|
}
|
|
4899
5091
|
}
|
|
4900
5092
|
const allModels = new Set([
|
|
4901
|
-
...
|
|
5093
|
+
...responseModelsForSignature,
|
|
4902
5094
|
...requestModelsForSignature,
|
|
4903
5095
|
...paramModels
|
|
4904
5096
|
]);
|
|
@@ -4909,12 +5101,8 @@ function generateResourceClass(service, ctx) {
|
|
|
4909
5101
|
lines.push("import { AutoPaginatable } from '../common/utils/pagination';");
|
|
4910
5102
|
lines.push("import { fetchAndDeserialize } from '../common/utils/fetch-and-deserialize';");
|
|
4911
5103
|
}
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
if (op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name)).length === 0) continue;
|
|
4915
|
-
const optionsName = paginatedOptionsName(method, resolvedName);
|
|
4916
|
-
lines.push(`import type { ${optionsName} } from './interfaces/${fileName$3(optionsName)}.interface';`);
|
|
4917
|
-
}
|
|
5104
|
+
const shouldEmitVaultCryptoHelpers = serviceClass === "Vault" && !ignoredMethodNames.has("encrypt") && !ignoredMethodNames.has("decrypt");
|
|
5105
|
+
if (shouldEmitVaultCryptoHelpers) lines.push("import { base64ToUint8Array, uint8ArrayToBase64 } from '../common/utils/base64';");
|
|
4918
5106
|
const hasIdempotentPost = plans.some((p) => p.plan.isIdempotentPost);
|
|
4919
5107
|
const hasCustomEncoding = plans.some((p) => p.op.requestBodyEncoding && p.op.requestBodyEncoding !== "json" && p.plan.hasBody);
|
|
4920
5108
|
if (hasIdempotentPost || hasCustomEncoding) lines.push("import type { PostOptions } from '../common/interfaces/post-options.interface';");
|
|
@@ -4922,7 +5110,16 @@ function generateResourceClass(service, ctx) {
|
|
|
4922
5110
|
for (const optionType of optionObjectTypes) {
|
|
4923
5111
|
if (importedTypeNames.has(optionType)) continue;
|
|
4924
5112
|
importedTypeNames.add(optionType);
|
|
4925
|
-
|
|
5113
|
+
const sourceFile = baselineTypeSourceFile(ctx, optionType);
|
|
5114
|
+
const relPath = sourceFile ? relativeImport(resourcePath, sourceFile) : `./interfaces/${fileName$3(optionType)}.interface`;
|
|
5115
|
+
lines.push(`import type { ${optionType} } from '${relPath}';`);
|
|
5116
|
+
}
|
|
5117
|
+
for (const typeName of returnTypeImports) {
|
|
5118
|
+
if (importedTypeNames.has(typeName)) continue;
|
|
5119
|
+
const sourceFile = baselineTypeSourceFile(ctx, typeName) ?? liveSurfaceInterfacePath(typeName);
|
|
5120
|
+
if (!sourceFile) continue;
|
|
5121
|
+
importedTypeNames.add(typeName);
|
|
5122
|
+
lines.push(`import type { ${typeName} } from '${relativeImport(resourcePath, sourceFile)}';`);
|
|
4926
5123
|
}
|
|
4927
5124
|
const { modelToService, resolveDir } = createServiceDirResolver(ctx.spec.models, ctx.spec.services, ctx);
|
|
4928
5125
|
const usedWireTypes = /* @__PURE__ */ new Set();
|
|
@@ -4954,6 +5151,17 @@ function generateResourceClass(service, ctx) {
|
|
|
4954
5151
|
lines.push(`import type { ${wireName} } from '${relPath}';`);
|
|
4955
5152
|
importedTypeNames.add(wireName);
|
|
4956
5153
|
}
|
|
5154
|
+
for (const name of responseModels) {
|
|
5155
|
+
if (allModels.has(name)) continue;
|
|
5156
|
+
const resolved = resolveInterfaceName(name, ctx);
|
|
5157
|
+
if (!usedWireTypes.has(resolved)) continue;
|
|
5158
|
+
const wireName = wireInterfaceName(resolved);
|
|
5159
|
+
if (importedTypeNames.has(wireName)) continue;
|
|
5160
|
+
const modelServiceDir = resolveDir(modelToService.get(name));
|
|
5161
|
+
const relPath = modelServiceDir === serviceDir ? `./interfaces/${fileName$3(name)}.interface` : `../${modelServiceDir}/interfaces/${fileName$3(name)}.interface`;
|
|
5162
|
+
lines.push(`import type { ${wireName} } from '${relPath}';`);
|
|
5163
|
+
importedTypeNames.add(wireName);
|
|
5164
|
+
}
|
|
4957
5165
|
for (const name of baselineResponseTypes) {
|
|
4958
5166
|
if (importedTypeNames.has(name)) continue;
|
|
4959
5167
|
const wireName = wireInterfaceName(name);
|
|
@@ -5017,47 +5225,64 @@ function generateResourceClass(service, ctx) {
|
|
|
5017
5225
|
}
|
|
5018
5226
|
}
|
|
5019
5227
|
lines.push("");
|
|
5020
|
-
for (const { op, plan, method } of plans)
|
|
5021
|
-
const
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
const
|
|
5032
|
-
const
|
|
5033
|
-
|
|
5228
|
+
for (const { op, plan, method } of plans) {
|
|
5229
|
+
const resolved = lookupResolved(op, resolvedLookup);
|
|
5230
|
+
const optionInfo = optionsObjectInfo(service, method, op, plan, ctx, baselineMethodFor$1(service, method, ctx), resolved);
|
|
5231
|
+
if (plan.isPaginated) {
|
|
5232
|
+
const extraParams = op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name));
|
|
5233
|
+
if (extraParams.length > 0) {
|
|
5234
|
+
const optionsName = optionInfo?.type ?? paginatedOptionsName(method, resolvedName);
|
|
5235
|
+
if (extraParams.some((p) => fieldName$6(p.name) !== wireFieldName(p.name))) {
|
|
5236
|
+
const serializerName = `serialize${optionsName}`;
|
|
5237
|
+
lines.push(`const ${serializerName} = (options: ${optionsName}): PaginationOptions => {`);
|
|
5238
|
+
lines.push(" const wire: Record<string, unknown> = {");
|
|
5239
|
+
const baselineFields = (ctx.apiSurface?.interfaces)?.[optionsName]?.fields;
|
|
5240
|
+
for (const p of PAGINATION_PARAM_NAMES) {
|
|
5241
|
+
if (baselineFields && !(p in baselineFields) && !baselineFields[toCamelCase(p)]) continue;
|
|
5242
|
+
lines.push(` ${p}: options.${p},`);
|
|
5243
|
+
}
|
|
5244
|
+
lines.push(" };");
|
|
5245
|
+
for (const param of extraParams) {
|
|
5246
|
+
const camel = fieldName$6(param.name);
|
|
5247
|
+
const snake = wireFieldName(param.name);
|
|
5248
|
+
lines.push(` if (options.${camel} !== undefined) wire.${snake} = options.${camel};`);
|
|
5249
|
+
}
|
|
5250
|
+
lines.push(" return wire as PaginationOptions;");
|
|
5251
|
+
lines.push("};");
|
|
5252
|
+
lines.push("");
|
|
5034
5253
|
}
|
|
5035
|
-
lines.push(" return wire as PaginationOptions;");
|
|
5036
|
-
lines.push("};");
|
|
5037
|
-
lines.push("");
|
|
5038
5254
|
}
|
|
5039
|
-
}
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5255
|
+
} else if (!optionInfo && !plan.isPaginated && !plan.hasBody && !plan.isDelete && op.queryParams.length > 0) {
|
|
5256
|
+
const resolved = lookupResolved(op, resolvedLookup);
|
|
5257
|
+
const opHiddenParams = new Set([...Object.keys(getOpDefaults(resolved)), ...getOpInferFromClient(resolved)]);
|
|
5258
|
+
const visibleParams = op.queryParams.filter((p) => !opHiddenParams.has(p.name));
|
|
5259
|
+
if (visibleParams.length > 0) {
|
|
5260
|
+
const optionsName = toPascalCase(method) + "Options";
|
|
5261
|
+
lines.push(`export interface ${optionsName} {`);
|
|
5262
|
+
for (const param of visibleParams) {
|
|
5263
|
+
const opt = !param.required ? "?" : "";
|
|
5264
|
+
if (param.description || param.deprecated) {
|
|
5265
|
+
const parts = [];
|
|
5266
|
+
if (param.description) parts.push(param.description);
|
|
5267
|
+
if (param.deprecated) parts.push("@deprecated");
|
|
5268
|
+
lines.push(...docComment$2(parts.join("\n"), 2));
|
|
5269
|
+
}
|
|
5270
|
+
lines.push(` ${fieldName$6(param.name)}${opt}: ${mapParamType(param.type, specEnumNames)};`);
|
|
5054
5271
|
}
|
|
5055
|
-
lines.push(
|
|
5272
|
+
lines.push("}");
|
|
5273
|
+
lines.push("");
|
|
5056
5274
|
}
|
|
5057
|
-
lines.push("}");
|
|
5058
|
-
lines.push("");
|
|
5059
5275
|
}
|
|
5060
5276
|
}
|
|
5277
|
+
if (shouldEmitVaultCryptoHelpers) {
|
|
5278
|
+
lines.push("export interface VaultEncryptedData {");
|
|
5279
|
+
lines.push(" ciphertext: string;");
|
|
5280
|
+
lines.push(" encryptedKeys: string;");
|
|
5281
|
+
lines.push(" iv: string;");
|
|
5282
|
+
lines.push(" tag: string;");
|
|
5283
|
+
lines.push("}");
|
|
5284
|
+
lines.push("");
|
|
5285
|
+
}
|
|
5061
5286
|
if (service.description) lines.push(...docComment$2(service.description));
|
|
5062
5287
|
lines.push(`export class ${serviceClass} {`);
|
|
5063
5288
|
lines.push(" constructor(private readonly workos: WorkOS) {}");
|
|
@@ -5067,6 +5292,10 @@ function generateResourceClass(service, ctx) {
|
|
|
5067
5292
|
lines.push(...renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames, resolved));
|
|
5068
5293
|
if (resolved?.wrappers && resolved.wrappers.length > 0) lines.push(...generateWrapperMethods$5(resolved, ctx));
|
|
5069
5294
|
}
|
|
5295
|
+
if (shouldEmitVaultCryptoHelpers) {
|
|
5296
|
+
lines.push("");
|
|
5297
|
+
lines.push(...renderVaultCryptoMethods());
|
|
5298
|
+
}
|
|
5070
5299
|
lines.push("}");
|
|
5071
5300
|
return {
|
|
5072
5301
|
path: resourcePath,
|
|
@@ -5074,6 +5303,51 @@ function generateResourceClass(service, ctx) {
|
|
|
5074
5303
|
skipIfExists: true
|
|
5075
5304
|
};
|
|
5076
5305
|
}
|
|
5306
|
+
function renderVaultCryptoMethods() {
|
|
5307
|
+
return [
|
|
5308
|
+
" async encrypt(",
|
|
5309
|
+
" plaintext: string,",
|
|
5310
|
+
" context: Record<string, string>,",
|
|
5311
|
+
" aad?: string,",
|
|
5312
|
+
" ): Promise<VaultEncryptedData> {",
|
|
5313
|
+
" const dataKeyPair = await this.createDataKey({ context });",
|
|
5314
|
+
" const encodedPlaintext = new TextEncoder().encode(plaintext);",
|
|
5315
|
+
" const encodedAad = aad ? new TextEncoder().encode(aad) : undefined;",
|
|
5316
|
+
" const encrypted = await this.workos.getCryptoProvider().encrypt(",
|
|
5317
|
+
" encodedPlaintext,",
|
|
5318
|
+
" base64ToUint8Array(dataKeyPair.dataKey.key),",
|
|
5319
|
+
" undefined,",
|
|
5320
|
+
" encodedAad,",
|
|
5321
|
+
" );",
|
|
5322
|
+
"",
|
|
5323
|
+
" return {",
|
|
5324
|
+
" ciphertext: uint8ArrayToBase64(encrypted.ciphertext),",
|
|
5325
|
+
" encryptedKeys: dataKeyPair.encryptedKeys,",
|
|
5326
|
+
" iv: uint8ArrayToBase64(encrypted.iv),",
|
|
5327
|
+
" tag: uint8ArrayToBase64(encrypted.tag),",
|
|
5328
|
+
" };",
|
|
5329
|
+
" }",
|
|
5330
|
+
"",
|
|
5331
|
+
" async decrypt(",
|
|
5332
|
+
" encryptedData: VaultEncryptedData,",
|
|
5333
|
+
" aad?: string,",
|
|
5334
|
+
" ): Promise<string> {",
|
|
5335
|
+
" const dataKey = await this.decryptDataKey({",
|
|
5336
|
+
" keys: encryptedData.encryptedKeys,",
|
|
5337
|
+
" });",
|
|
5338
|
+
" const encodedAad = aad ? new TextEncoder().encode(aad) : undefined;",
|
|
5339
|
+
" const decrypted = await this.workos.getCryptoProvider().decrypt(",
|
|
5340
|
+
" base64ToUint8Array(encryptedData.ciphertext),",
|
|
5341
|
+
" base64ToUint8Array(dataKey.key),",
|
|
5342
|
+
" base64ToUint8Array(encryptedData.iv),",
|
|
5343
|
+
" base64ToUint8Array(encryptedData.tag),",
|
|
5344
|
+
" encodedAad,",
|
|
5345
|
+
" );",
|
|
5346
|
+
"",
|
|
5347
|
+
" return new TextDecoder().decode(decrypted);",
|
|
5348
|
+
" }"
|
|
5349
|
+
];
|
|
5350
|
+
}
|
|
5077
5351
|
function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames, resolvedOp) {
|
|
5078
5352
|
const lines = [];
|
|
5079
5353
|
const responseModel = plan.responseModelName ? resolveInterfaceName(plan.responseModelName, ctx) : null;
|
|
@@ -5081,9 +5355,11 @@ function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames,
|
|
|
5081
5355
|
const hiddenParams = new Set([...Object.keys(getOpDefaults(resolvedOp)), ...getOpInferFromClient(resolvedOp)]);
|
|
5082
5356
|
const httpKey = `${op.httpMethod.toUpperCase()} ${op.path}`;
|
|
5083
5357
|
const baselineClassMethod = baselineMethodFor$1(service, method, ctx);
|
|
5358
|
+
const optionInfo = optionsObjectInfo(service, method, op, plan, ctx, baselineClassMethod, resolvedOp);
|
|
5084
5359
|
const overlayMethod = ctx.overlayLookup?.methodByOperation?.get(httpKey) ?? baselineClassMethod;
|
|
5085
5360
|
let validParamNames = null;
|
|
5086
|
-
if (
|
|
5361
|
+
if (optionInfo) validParamNames = new Set(["options"]);
|
|
5362
|
+
else if (overlayMethod) validParamNames = new Set(overlayMethod.params.map((p) => p.name));
|
|
5087
5363
|
else {
|
|
5088
5364
|
const actualParams = /* @__PURE__ */ new Set();
|
|
5089
5365
|
for (const p of op.pathParams) actualParams.add(fieldName$6(p.name));
|
|
@@ -5197,7 +5473,7 @@ function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames,
|
|
|
5197
5473
|
const unwrapped = unwrapListModel$4(pModel, modelMap);
|
|
5198
5474
|
if (unwrapped) itemRawName = unwrapped.name;
|
|
5199
5475
|
}
|
|
5200
|
-
const itemTypeName = resolveInterfaceName(itemRawName, ctx);
|
|
5476
|
+
const itemTypeName = preferredBaselineTypeName(ctx, autoPaginatableItemType$1(baselineClassMethod?.returnType) ?? autoPaginatableItemType$1(overlayMethod?.returnType)) ?? resolveInterfaceName(itemRawName, ctx);
|
|
5201
5477
|
docParts.push(`@returns {Promise<AutoPaginatable<${itemTypeName}>>}`);
|
|
5202
5478
|
} else if (responseModel) {
|
|
5203
5479
|
const returnTypeDoc = plan.isArrayResponse ? `${responseModel}[]` : responseModel;
|
|
@@ -5219,7 +5495,7 @@ function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames,
|
|
|
5219
5495
|
}
|
|
5220
5496
|
}
|
|
5221
5497
|
}
|
|
5222
|
-
if (renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelMap, specEnumNames, baselineClassMethod)) return lines;
|
|
5498
|
+
if (renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelMap, specEnumNames, baselineClassMethod, resolvedOp)) return lines;
|
|
5223
5499
|
const preDecisionCount = lines.length;
|
|
5224
5500
|
if (plan.isPaginated && op.pagination && op.httpMethod === "get") {
|
|
5225
5501
|
let paginatedItemRawName = op.pagination.itemType.kind === "model" ? op.pagination.itemType.name : null;
|
|
@@ -5246,12 +5522,17 @@ function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames,
|
|
|
5246
5522
|
}
|
|
5247
5523
|
return lines;
|
|
5248
5524
|
}
|
|
5249
|
-
function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelMap, specEnumNames, baselineMethod) {
|
|
5250
|
-
const optionParam =
|
|
5525
|
+
function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelMap, specEnumNames, baselineMethod, resolvedOp) {
|
|
5526
|
+
const optionParam = optionsObjectInfo(service, method, op, plan, ctx, baselineMethod, resolvedOp);
|
|
5251
5527
|
if (!optionParam) return false;
|
|
5252
5528
|
const responseModel = plan.responseModelName ? resolveInterfaceName(plan.responseModelName, ctx) : null;
|
|
5253
5529
|
const pathBindings = buildOptionsObjectPathBindings(op, optionParam.type, ctx);
|
|
5254
5530
|
const pathStr = buildPathStr(op, buildOptionsObjectPathParamMap(op, optionParam.type, ctx));
|
|
5531
|
+
const override = operationOverrideFor$1(ctx, op);
|
|
5532
|
+
const bodyFieldMap = override?.bodyFieldMap;
|
|
5533
|
+
const visibleQueryParams = visibleQueryParamsForOptions(op, plan, resolvedOp);
|
|
5534
|
+
const queryBindings = visibleQueryParams.map((param) => fieldName$6(param.name));
|
|
5535
|
+
const queryOptionsArg = visibleQueryParams.length > 0 || Object.keys(getOpDefaults(resolvedOp)).length > 0 || getOpInferFromClient(resolvedOp).length > 0 ? `, { query: ${renderQueryExprWithOptions(visibleQueryParams, optionParam.optional, resolvedOp)} }` : "";
|
|
5255
5536
|
if (plan.isPaginated && op.pagination && op.httpMethod === "get") {
|
|
5256
5537
|
let itemRawName = op.pagination.itemType.kind === "model" ? op.pagination.itemType.name : null;
|
|
5257
5538
|
if (itemRawName) {
|
|
@@ -5265,14 +5546,16 @@ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelM
|
|
|
5265
5546
|
if (!itemType) return false;
|
|
5266
5547
|
const wireType = wireInterfaceName(itemType);
|
|
5267
5548
|
const returnType = preferredBaselineReturnType(ctx, baselineMethod?.returnType) ?? `Promise<AutoPaginatable<${itemType}>>`;
|
|
5268
|
-
|
|
5269
|
-
|
|
5549
|
+
const needsWireSerializer = op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name)).some((p) => fieldName$6(p.name) !== wireFieldName(p.name));
|
|
5550
|
+
const listOptionsExpr = needsWireSerializer ? `options ? serialize${optionParam.type}(options) : undefined` : "paginationOptions";
|
|
5551
|
+
lines.push(` async ${method}(${renderOptionsParam(optionParam)}): ${returnType} {`);
|
|
5552
|
+
renderOptionsObjectDestructure(lines, pathBindings, needsWireSerializer ? void 0 : "paginationOptions");
|
|
5270
5553
|
lines.push(` return new AutoPaginatable(`);
|
|
5271
5554
|
lines.push(` await fetchAndDeserialize<${wireType}, ${itemType}>(`);
|
|
5272
5555
|
lines.push(` this.workos,`);
|
|
5273
5556
|
lines.push(` ${pathStr},`);
|
|
5274
5557
|
lines.push(` deserialize${itemType},`);
|
|
5275
|
-
lines.push(`
|
|
5558
|
+
lines.push(` ${listOptionsExpr},`);
|
|
5276
5559
|
lines.push(` ),`);
|
|
5277
5560
|
lines.push(` (params) =>`);
|
|
5278
5561
|
lines.push(` fetchAndDeserialize<${wireType}, ${itemType}>(`);
|
|
@@ -5281,15 +5564,15 @@ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelM
|
|
|
5281
5564
|
lines.push(` deserialize${itemType},`);
|
|
5282
5565
|
lines.push(` params,`);
|
|
5283
5566
|
lines.push(` ),`);
|
|
5284
|
-
lines.push(`
|
|
5567
|
+
lines.push(` ${listOptionsExpr},`);
|
|
5285
5568
|
lines.push(` );`);
|
|
5286
5569
|
lines.push(" }");
|
|
5287
5570
|
return true;
|
|
5288
5571
|
}
|
|
5289
5572
|
if (plan.isDelete && !plan.hasBody) {
|
|
5290
|
-
lines.push(` async ${method}(
|
|
5573
|
+
lines.push(` async ${method}(${renderOptionsParam(optionParam)}): Promise<void> {`);
|
|
5291
5574
|
renderOptionsObjectDestructure(lines, pathBindings);
|
|
5292
|
-
lines.push(` await this.workos.delete(${pathStr});`);
|
|
5575
|
+
lines.push(` await this.workos.delete(${pathStr}${queryOptionsArg});`);
|
|
5293
5576
|
lines.push(" }");
|
|
5294
5577
|
return true;
|
|
5295
5578
|
}
|
|
@@ -5312,40 +5595,56 @@ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelM
|
|
|
5312
5595
|
entityType = requestType;
|
|
5313
5596
|
}
|
|
5314
5597
|
if (plan.isDelete) {
|
|
5315
|
-
lines.push(` async ${method}(
|
|
5316
|
-
renderOptionsObjectDestructure(lines, pathBindings, "payload");
|
|
5317
|
-
|
|
5598
|
+
lines.push(` async ${method}(${renderOptionsParam(optionParam)}): Promise<void> {`);
|
|
5599
|
+
renderOptionsObjectDestructure(lines, [...pathBindings, ...queryBindings], "payload");
|
|
5600
|
+
const bodyParam = renderOptionsObjectBodyFieldMap(lines, bodyFieldMap);
|
|
5601
|
+
bodyExpr = bodyArgExprWithParam(bodyExpr, bodyParam);
|
|
5602
|
+
lines.push(` await this.workos.deleteWithBody<${entityType}>(${pathStr}, ${bodyExpr}${queryOptionsArg});`);
|
|
5318
5603
|
lines.push(" }");
|
|
5319
5604
|
return true;
|
|
5320
5605
|
}
|
|
5321
5606
|
if (!responseModel) {
|
|
5322
|
-
lines.push(` async ${method}(
|
|
5323
|
-
renderOptionsObjectDestructure(lines, pathBindings, "payload");
|
|
5324
|
-
|
|
5607
|
+
lines.push(` async ${method}(${renderOptionsParam(optionParam)}): Promise<void> {`);
|
|
5608
|
+
renderOptionsObjectDestructure(lines, [...pathBindings, ...queryBindings], "payload");
|
|
5609
|
+
const bodyParam = renderOptionsObjectBodyFieldMap(lines, bodyFieldMap);
|
|
5610
|
+
bodyExpr = bodyArgExprWithParam(bodyExpr, bodyParam);
|
|
5611
|
+
lines.push(` await this.workos.${op.httpMethod}<void, ${entityType}>(${pathStr}, ${bodyExpr}${queryOptionsArg});`);
|
|
5325
5612
|
lines.push(" }");
|
|
5326
5613
|
return true;
|
|
5327
5614
|
}
|
|
5328
5615
|
const returnType = plan.isArrayResponse ? `${responseModel}[]` : responseModel;
|
|
5616
|
+
const methodReturnType = override?.returnType ?? `Promise<${returnType}>`;
|
|
5329
5617
|
const wireType = plan.isArrayResponse ? `${wireInterfaceName(responseModel)}[]` : wireInterfaceName(responseModel);
|
|
5330
5618
|
const returnExpr = plan.isArrayResponse ? `data.map(deserialize${responseModel})` : `deserialize${responseModel}(data)`;
|
|
5331
|
-
|
|
5332
|
-
|
|
5619
|
+
const finalReturnExpr = override?.returnDataProperty ? `${returnExpr}.${override.returnDataProperty}` : returnExpr;
|
|
5620
|
+
lines.push(` async ${method}(${renderOptionsParam(optionParam)}): ${methodReturnType} {`);
|
|
5621
|
+
renderOptionsObjectDestructure(lines, [...pathBindings, ...queryBindings], "payload");
|
|
5622
|
+
const bodyParam = renderOptionsObjectBodyFieldMap(lines, bodyFieldMap);
|
|
5623
|
+
bodyExpr = bodyArgExprWithParam(bodyExpr, bodyParam);
|
|
5333
5624
|
lines.push(` const { data } = await this.workos.${op.httpMethod}<${wireType}, ${entityType}>(`);
|
|
5334
5625
|
lines.push(` ${pathStr},`);
|
|
5335
|
-
lines.push(` ${bodyExpr},`);
|
|
5626
|
+
lines.push(` ${bodyExpr}${queryOptionsArg},`);
|
|
5336
5627
|
lines.push(" );");
|
|
5337
|
-
|
|
5628
|
+
if (override?.returnExpression) {
|
|
5629
|
+
lines.push(` const result = ${returnExpr};`);
|
|
5630
|
+
lines.push(` return ${override.returnExpression};`);
|
|
5631
|
+
} else lines.push(` return ${finalReturnExpr};`);
|
|
5338
5632
|
lines.push(" }");
|
|
5339
5633
|
return true;
|
|
5340
5634
|
}
|
|
5341
5635
|
if (responseModel) {
|
|
5342
5636
|
const returnType = plan.isArrayResponse ? `${responseModel}[]` : responseModel;
|
|
5637
|
+
const methodReturnType = override?.returnType ?? `Promise<${returnType}>`;
|
|
5343
5638
|
const wireType = plan.isArrayResponse ? `${wireInterfaceName(responseModel)}[]` : wireInterfaceName(responseModel);
|
|
5344
5639
|
const returnExpr = plan.isArrayResponse ? `data.map(deserialize${responseModel})` : `deserialize${responseModel}(data)`;
|
|
5345
|
-
|
|
5640
|
+
const finalReturnExpr = override?.returnDataProperty ? `${returnExpr}.${override.returnDataProperty}` : returnExpr;
|
|
5641
|
+
lines.push(` async ${method}(${renderOptionsParam(optionParam)}): ${methodReturnType} {`);
|
|
5346
5642
|
renderOptionsObjectDestructure(lines, pathBindings);
|
|
5347
|
-
lines.push(` const { data } = await this.workos.${op.httpMethod}<${wireType}>(${pathStr});`);
|
|
5348
|
-
|
|
5643
|
+
lines.push(` const { data } = await this.workos.${op.httpMethod}<${wireType}>(${pathStr}${queryOptionsArg});`);
|
|
5644
|
+
if (override?.returnExpression) {
|
|
5645
|
+
lines.push(` const result = ${returnExpr};`);
|
|
5646
|
+
lines.push(` return ${override.returnExpression};`);
|
|
5647
|
+
} else lines.push(` return ${finalReturnExpr};`);
|
|
5349
5648
|
lines.push(" }");
|
|
5350
5649
|
return true;
|
|
5351
5650
|
}
|
|
@@ -5356,6 +5655,18 @@ function renderOptionsObjectDestructure(lines, pathBindings, restName) {
|
|
|
5356
5655
|
else if (pathBindings.length > 0) lines.push(` const { ${pathBindings.join(", ")} } = options;`);
|
|
5357
5656
|
else if (restName) lines.push(` const ${restName} = options;`);
|
|
5358
5657
|
}
|
|
5658
|
+
function renderOptionsObjectBodyFieldMap(lines, bodyFieldMap) {
|
|
5659
|
+
const entries = Object.entries(bodyFieldMap ?? {});
|
|
5660
|
+
if (entries.length === 0) return "payload";
|
|
5661
|
+
lines.push(" const requestPayload = {");
|
|
5662
|
+
lines.push(" ...payload,");
|
|
5663
|
+
for (const [source, target] of entries) lines.push(` ${target}: payload.${source},`);
|
|
5664
|
+
lines.push(" };");
|
|
5665
|
+
return "requestPayload";
|
|
5666
|
+
}
|
|
5667
|
+
function bodyArgExprWithParam(bodyExpr, paramName) {
|
|
5668
|
+
return paramName === "payload" ? bodyExpr : bodyExpr.replace(/\bpayload\b/g, paramName);
|
|
5669
|
+
}
|
|
5359
5670
|
function buildOptionsObjectPathBindings(op, optionType, ctx) {
|
|
5360
5671
|
return op.pathParams.map((param) => resolveOptionsObjectField$1(fieldName$6(param.name), optionType, ctx));
|
|
5361
5672
|
}
|
|
@@ -5405,7 +5716,7 @@ function renderPaginatedMethod(lines, op, plan, method, itemType, pathStr, resol
|
|
|
5405
5716
|
lines.push(` deserialize${itemType},`);
|
|
5406
5717
|
lines.push(` params,`);
|
|
5407
5718
|
lines.push(` ),`);
|
|
5408
|
-
lines.push(`
|
|
5719
|
+
lines.push(` ${serializeCall},`);
|
|
5409
5720
|
lines.push(` );`);
|
|
5410
5721
|
lines.push(" }");
|
|
5411
5722
|
}
|
|
@@ -5634,6 +5945,20 @@ function renderQueryExpr(queryParams) {
|
|
|
5634
5945
|
}
|
|
5635
5946
|
return `options ? { ${parts.join(", ")} } : undefined`;
|
|
5636
5947
|
}
|
|
5948
|
+
function renderQueryExprWithOptions(queryParams, optional, resolvedOp) {
|
|
5949
|
+
const parts = [];
|
|
5950
|
+
for (const param of queryParams) {
|
|
5951
|
+
const camel = fieldName$6(param.name);
|
|
5952
|
+
const snake = wireFieldName(param.name);
|
|
5953
|
+
const access = optional ? `options?.${camel}` : `options.${camel}`;
|
|
5954
|
+
if (param.required) parts.push(`${snake}: ${access}`);
|
|
5955
|
+
else parts.push(`...(${access} !== undefined && { ${snake}: ${access} })`);
|
|
5956
|
+
}
|
|
5957
|
+
for (const [key, value] of Object.entries(getOpDefaults(resolvedOp))) parts.push(`${key}: ${tsLiteral(value)}`);
|
|
5958
|
+
for (const field of getOpInferFromClient(resolvedOp)) parts.push(`${field}: ${clientFieldExpression$8(field)}`);
|
|
5959
|
+
if (parts.length === 0) return optional ? "options" : "{}";
|
|
5960
|
+
return `{ ${parts.join(", ")} }`;
|
|
5961
|
+
}
|
|
5637
5962
|
function buildPathStr(op, paramNameMap) {
|
|
5638
5963
|
return buildNodePathExpression(op.path, paramNameMap);
|
|
5639
5964
|
}
|
|
@@ -6074,6 +6399,17 @@ function generateModels$7(models, ctx, shared) {
|
|
|
6074
6399
|
const discriminatedSkip = ctx._discriminatedModelNames;
|
|
6075
6400
|
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
6076
6401
|
const listMetadataNeeded = collectReferencedListMetadataModels(models, nonPaginatedRefs);
|
|
6402
|
+
for (const originalModel of models) {
|
|
6403
|
+
const model = projectedByName.get(originalModel.name) ?? originalModel;
|
|
6404
|
+
if (!reachableModels.has(model.name)) continue;
|
|
6405
|
+
if (interfaceEligibleModels && !interfaceEligibleModels.has(model.name)) continue;
|
|
6406
|
+
if (!isNodeOwnedService(ctx, modelToService.get(model.name)) && !modelHasNewFields(model, ctx) && !forceGenerate.has(model.name)) continue;
|
|
6407
|
+
const canonicalName = dedup.get(model.name);
|
|
6408
|
+
if (canonicalName) {
|
|
6409
|
+
forceGenerate.add(canonicalName);
|
|
6410
|
+
if (interfaceEligibleModels) interfaceEligibleModels.add(canonicalName);
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6077
6413
|
for (const originalModel of models) {
|
|
6078
6414
|
const model = projectedByName.get(originalModel.name) ?? originalModel;
|
|
6079
6415
|
if (!reachableModels.has(model.name)) continue;
|
|
@@ -6200,9 +6536,11 @@ function generateModels$7(models, ctx, shared) {
|
|
|
6200
6536
|
}
|
|
6201
6537
|
for (const dep of deps.models) {
|
|
6202
6538
|
const depName = resolveInterfaceName(dep, ctx);
|
|
6203
|
-
const
|
|
6539
|
+
const depService = modelToService.get(dep);
|
|
6540
|
+
const depDir = resolveDir(depService);
|
|
6541
|
+
const depIsOwned = isNodeOwnedService(ctx, depService);
|
|
6204
6542
|
const currentFilePath = `src/${dirName}/interfaces/${fileName$3(model.name)}.interface.ts`;
|
|
6205
|
-
const baselineSrc = (ctx.apiSurface?.interfaces?.[depName])?.sourceFile;
|
|
6543
|
+
const baselineSrc = depIsOwned ? void 0 : (ctx.apiSurface?.interfaces?.[depName])?.sourceFile;
|
|
6206
6544
|
if (baselineSrc === currentFilePath) continue;
|
|
6207
6545
|
let relPath;
|
|
6208
6546
|
if (baselineSrc) relPath = relativeImport(currentFilePath, baselineSrc).replace(/\.ts$/, "");
|
|
@@ -6385,6 +6723,7 @@ function generateSerializers(models, ctx, shared) {
|
|
|
6385
6723
|
const resourceUsage = buildGeneratedResourceModelUsage(models, ctx);
|
|
6386
6724
|
const serializerEligibleModels = resourceUsage ? expandModelRoots(resourceUsage.serializerRoots, projectedByName) : void 0;
|
|
6387
6725
|
const responseReachableModels = resourceUsage ? expandModelRoots(resourceUsage.responseRoots, projectedByName) : void 0;
|
|
6726
|
+
const requestReachableModels = resourceUsage ? expandModelRoots(resourceUsage.requestRoots, projectedByName) : void 0;
|
|
6388
6727
|
const serializerReachable = computeNonEventReachable(ctx.spec.services, models);
|
|
6389
6728
|
const liveRoot = ctx.targetDir ?? ctx.outputDir;
|
|
6390
6729
|
if (liveRoot) for (const originalModel of models) {
|
|
@@ -6417,6 +6756,17 @@ function generateSerializers(models, ctx, shared) {
|
|
|
6417
6756
|
const discriminatedSerializerSkip = ctx._discriminatedModelNames;
|
|
6418
6757
|
const serializerNonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
6419
6758
|
const serializerListMetadataNeeded = collectReferencedListMetadataModels(models, serializerNonPaginatedRefs);
|
|
6759
|
+
for (const originalModel of models) {
|
|
6760
|
+
const model = projectedByName.get(originalModel.name) ?? originalModel;
|
|
6761
|
+
if (!serializerReachable.has(model.name)) continue;
|
|
6762
|
+
if (serializerEligibleModels && !serializerEligibleModels.has(model.name)) continue;
|
|
6763
|
+
if (!isNodeOwnedService(ctx, modelToService.get(model.name)) && !modelHasNewFields(model, ctx) && !forceGenerateSerializer.has(model.name)) continue;
|
|
6764
|
+
const canonicalName = dedup.get(model.name);
|
|
6765
|
+
if (canonicalName) {
|
|
6766
|
+
forceGenerateSerializer.add(canonicalName);
|
|
6767
|
+
if (serializerEligibleModels) serializerEligibleModels.add(canonicalName);
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6420
6770
|
const eligibleModels = [];
|
|
6421
6771
|
for (const originalModel of models) {
|
|
6422
6772
|
const model = projectedByName.get(originalModel.name) ?? originalModel;
|
|
@@ -6435,7 +6785,7 @@ function generateSerializers(models, ctx, shared) {
|
|
|
6435
6785
|
const responseName = wireInterfaceName(domainName);
|
|
6436
6786
|
const baselineResponse = ctx.apiSurface?.interfaces?.[responseName];
|
|
6437
6787
|
const baselineDomain = ctx.apiSurface?.interfaces?.[domainName];
|
|
6438
|
-
if (shouldSkipSerializeForModel(model, baselineResponse, baselineDomain, dedup, skippedSerializeModels, ctx)) skippedSerializeModels.add(model.name);
|
|
6788
|
+
if (requestReachableModels !== void 0 && !requestReachableModels.has(model.name) || shouldSkipSerializeForModel(model, baselineResponse, baselineDomain, dedup, skippedSerializeModels, ctx)) skippedSerializeModels.add(model.name);
|
|
6439
6789
|
}
|
|
6440
6790
|
for (const model of eligibleModels) {
|
|
6441
6791
|
const service = modelToService.get(model.name);
|
|
@@ -6451,20 +6801,24 @@ function generateSerializers(models, ctx, shared) {
|
|
|
6451
6801
|
const canonSerializerPath = `src/${canonDir}/serializers/${fileName$3(canonicalName)}.serializer.ts`;
|
|
6452
6802
|
if (serializerPath === canonSerializerPath) continue;
|
|
6453
6803
|
if (domainName === canonDomainName) continue;
|
|
6454
|
-
const
|
|
6455
|
-
const
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6804
|
+
const aliasNeedsDeserialize = responseReachableModels === void 0 || responseReachableModels.has(model.name);
|
|
6805
|
+
const canonicalHasDeserialize = responseReachableModels === void 0 || responseReachableModels.has(canonicalName);
|
|
6806
|
+
if (!aliasNeedsDeserialize || canonicalHasDeserialize) {
|
|
6807
|
+
const rel = relativeImport(serializerPath, canonSerializerPath);
|
|
6808
|
+
const canonSkipSerialize = skippedSerializeModels.has(canonicalName) || skippedSerializeModels.has(model.name);
|
|
6809
|
+
const canonSkipDeserialize = responseReachableModels !== void 0 && !responseReachableModels.has(canonicalName) && !responseReachableModels.has(model.name);
|
|
6810
|
+
if (canonSkipSerialize && canonSkipDeserialize) continue;
|
|
6811
|
+
const parts = [];
|
|
6812
|
+
if (!canonSkipDeserialize) parts.push(`deserialize${canonDomainName} as deserialize${domainName}`);
|
|
6813
|
+
if (!canonSkipSerialize) parts.push(`serialize${canonDomainName} as serialize${domainName}`);
|
|
6814
|
+
const reexportContent = `export { ${parts.join(", ")} } from '${rel}';`;
|
|
6815
|
+
files.push({
|
|
6816
|
+
path: serializerPath,
|
|
6817
|
+
content: reexportContent,
|
|
6818
|
+
overwriteExisting: true
|
|
6819
|
+
});
|
|
6820
|
+
continue;
|
|
6821
|
+
}
|
|
6468
6822
|
}
|
|
6469
6823
|
const dirName = resolveDir(service);
|
|
6470
6824
|
const isDedupCanonical = [...dedup.values()].includes(model.name);
|
|
@@ -6504,7 +6858,7 @@ function generateSerializers(models, ctx, shared) {
|
|
|
6504
6858
|
}
|
|
6505
6859
|
const liveRootForBarrel = ctx.outputDir ?? ctx.targetDir;
|
|
6506
6860
|
for (const [dir, stems] of serializersByDir) {
|
|
6507
|
-
if (liveRootForBarrel) {
|
|
6861
|
+
if (liveRootForBarrel && !isNodeOwnedService(ctx, dir)) {
|
|
6508
6862
|
const serializersDir = path.join(liveRootForBarrel, "src", dir, "serializers");
|
|
6509
6863
|
try {
|
|
6510
6864
|
for (const entry of fs.readdirSync(serializersDir)) {
|
|
@@ -6750,6 +7104,25 @@ function generateWorkOSClient$1(spec, ctx) {
|
|
|
6750
7104
|
skipIfExists: true
|
|
6751
7105
|
};
|
|
6752
7106
|
}
|
|
7107
|
+
function sourceFileForExportedType(ctx, typeName) {
|
|
7108
|
+
return (ctx.apiSurface?.interfaces?.[typeName])?.sourceFile ?? (ctx.apiSurface?.typeAliases?.[typeName])?.sourceFile ?? (ctx.apiSurface?.enums?.[typeName])?.sourceFile ?? liveSurfaceInterfacePath(typeName);
|
|
7109
|
+
}
|
|
7110
|
+
function exportedNamesForSource(ctx, sourceFile) {
|
|
7111
|
+
const names = /* @__PURE__ */ new Set();
|
|
7112
|
+
const addNamesFromSurface = (items) => {
|
|
7113
|
+
if (!items) return;
|
|
7114
|
+
for (const [name, item] of Object.entries(items)) if (item.sourceFile === sourceFile) names.add(name);
|
|
7115
|
+
};
|
|
7116
|
+
addNamesFromSurface(ctx.apiSurface?.interfaces);
|
|
7117
|
+
addNamesFromSurface(ctx.apiSurface?.typeAliases);
|
|
7118
|
+
addNamesFromSurface(ctx.apiSurface?.enums);
|
|
7119
|
+
const rootDir = ctx.targetDir ?? ctx.outputDir;
|
|
7120
|
+
if (rootDir) try {
|
|
7121
|
+
const content = fs.readFileSync(path.join(rootDir, sourceFile), "utf-8");
|
|
7122
|
+
for (const match of content.matchAll(/export\s+(?:interface|type|enum|class|const|function)\s+(\w+)/g)) names.add(match[1]);
|
|
7123
|
+
} catch {}
|
|
7124
|
+
return [...names];
|
|
7125
|
+
}
|
|
6753
7126
|
/**
|
|
6754
7127
|
* Generate per-service barrel files (interfaces/index.ts) that re-export
|
|
6755
7128
|
* all interface and enum files for each service directory. This reduces
|
|
@@ -6762,11 +7135,21 @@ function generateServiceBarrels(spec, ctx) {
|
|
|
6762
7135
|
const enumToService = assignEnumsToServices(spec.enums, spec.services, spec.models, ctx);
|
|
6763
7136
|
const dirExports = /* @__PURE__ */ new Map();
|
|
6764
7137
|
const dirSymbols = /* @__PURE__ */ new Map();
|
|
7138
|
+
const ownedDirNames = /* @__PURE__ */ new Set();
|
|
7139
|
+
for (const service of spec.services) if (isNodeOwnedService(ctx, service.name)) {
|
|
7140
|
+
const dir = resolveDir(service.name);
|
|
7141
|
+
ownedDirNames.add(dir);
|
|
7142
|
+
if (!dirExports.has(dir)) {
|
|
7143
|
+
dirExports.set(dir, []);
|
|
7144
|
+
if (!dirSymbols.has(dir)) dirSymbols.set(dir, /* @__PURE__ */ new Set());
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
6765
7147
|
const dirSymbolsFromBaseline = /* @__PURE__ */ new Map();
|
|
6766
7148
|
const seedFromBaseline = (sourceFile, name) => {
|
|
6767
7149
|
const match = sourceFile.match(/^src\/([^/]+)\/interfaces\/(.+)\.ts$/);
|
|
6768
7150
|
if (!match) return;
|
|
6769
7151
|
const dirName = match[1];
|
|
7152
|
+
if (ownedDirNames.has(dirName)) return;
|
|
6770
7153
|
const fileStem = match[2];
|
|
6771
7154
|
if (!dirSymbols.has(dirName)) dirSymbols.set(dirName, /* @__PURE__ */ new Set());
|
|
6772
7155
|
dirSymbols.get(dirName).add(name);
|
|
@@ -6819,9 +7202,63 @@ function generateServiceBarrels(spec, ctx) {
|
|
|
6819
7202
|
globalExistingSymbols.add(enumDef.name);
|
|
6820
7203
|
dirExports.get(dirName).push(`export * from './${fileName$3(enumDef.name)}.interface';`);
|
|
6821
7204
|
}
|
|
7205
|
+
const overrideWildcardSources = /* @__PURE__ */ new Set();
|
|
7206
|
+
const overrideNamedExports = /* @__PURE__ */ new Set();
|
|
7207
|
+
const addOverrideTypeExport = (typeName) => {
|
|
7208
|
+
if (!typeName) return;
|
|
7209
|
+
const sourceFile = sourceFileForExportedType(ctx, typeName);
|
|
7210
|
+
if (!sourceFile) return;
|
|
7211
|
+
const match = sourceFile?.match(/^src\/([^/]+)\/interfaces\/(.+)\.ts$/);
|
|
7212
|
+
if (!match) return;
|
|
7213
|
+
const dirName = match[1];
|
|
7214
|
+
const stem = match[2].replace(/\.ts$/, "");
|
|
7215
|
+
if (!dirExports.has(dirName)) {
|
|
7216
|
+
dirExports.set(dirName, []);
|
|
7217
|
+
if (!dirSymbols.has(dirName)) dirSymbols.set(dirName, /* @__PURE__ */ new Set());
|
|
7218
|
+
}
|
|
7219
|
+
const symbols = dirSymbols.get(dirName);
|
|
7220
|
+
const sourceKey = `${dirName}/${stem}`;
|
|
7221
|
+
if (overrideWildcardSources.has(sourceKey)) return;
|
|
7222
|
+
const sourceNames = exportedNamesForSource(ctx, sourceFile);
|
|
7223
|
+
if (sourceNames.some((name) => name !== typeName && globalExistingSymbols.has(name))) {
|
|
7224
|
+
const exportKey = `${sourceKey}:${typeName}`;
|
|
7225
|
+
if (overrideNamedExports.has(exportKey)) return;
|
|
7226
|
+
dirExports.get(dirName).push(`export type { ${typeName} } from './${stem}';`);
|
|
7227
|
+
overrideNamedExports.add(exportKey);
|
|
7228
|
+
symbols.add(typeName);
|
|
7229
|
+
globalExistingSymbols.add(typeName);
|
|
7230
|
+
return;
|
|
7231
|
+
}
|
|
7232
|
+
dirExports.get(dirName).push(`export * from './${stem}';`);
|
|
7233
|
+
overrideWildcardSources.add(sourceKey);
|
|
7234
|
+
const namesToRegister = sourceNames.length > 0 ? sourceNames : [typeName];
|
|
7235
|
+
for (const name of namesToRegister) {
|
|
7236
|
+
symbols.add(name);
|
|
7237
|
+
globalExistingSymbols.add(name);
|
|
7238
|
+
}
|
|
7239
|
+
};
|
|
7240
|
+
for (const override of Object.values(nodeOptions(ctx).operationOverrides ?? {})) {
|
|
7241
|
+
addOverrideTypeExport(override.optionsType);
|
|
7242
|
+
for (const typeName of override.returnTypeImports ?? []) addOverrideTypeExport(typeName);
|
|
7243
|
+
}
|
|
7244
|
+
const generatedOptionExports = ctx._nodeGeneratedOptionInterfaceExports;
|
|
7245
|
+
for (const [dirName, options] of generatedOptionExports ?? []) {
|
|
7246
|
+
if (!dirExports.has(dirName)) {
|
|
7247
|
+
dirExports.set(dirName, []);
|
|
7248
|
+
if (!dirSymbols.has(dirName)) dirSymbols.set(dirName, /* @__PURE__ */ new Set());
|
|
7249
|
+
}
|
|
7250
|
+
const symbols = dirSymbols.get(dirName);
|
|
7251
|
+
for (const { stem, typeName } of options) {
|
|
7252
|
+
if (globalExistingSymbols.has(typeName)) continue;
|
|
7253
|
+
symbols.add(typeName);
|
|
7254
|
+
globalExistingSymbols.add(typeName);
|
|
7255
|
+
dirExports.get(dirName).push(`export * from './${stem}';`);
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
6822
7258
|
for (const [dirName, exports] of dirExports) {
|
|
6823
7259
|
const exportSet = new Set(exports);
|
|
6824
|
-
|
|
7260
|
+
const isDirOwned = ownedDirNames.has(dirName);
|
|
7261
|
+
if (ctx.apiSurface && !isDirOwned) {
|
|
6825
7262
|
const addBaselineExports = (items) => {
|
|
6826
7263
|
if (!items) return;
|
|
6827
7264
|
for (const item of Object.values(items)) {
|
|
@@ -6834,7 +7271,7 @@ function generateServiceBarrels(spec, ctx) {
|
|
|
6834
7271
|
addBaselineExports(ctx.apiSurface.interfaces);
|
|
6835
7272
|
addBaselineExports(ctx.apiSurface.typeAliases);
|
|
6836
7273
|
addBaselineExports(ctx.apiSurface.enums);
|
|
6837
|
-
if (ctx.targetDir) {
|
|
7274
|
+
if (ctx.targetDir && !isDirOwned) {
|
|
6838
7275
|
const interfacesDir = path.join(ctx.targetDir, "src", dirName, "interfaces");
|
|
6839
7276
|
try {
|
|
6840
7277
|
const barrelPath = path.join(interfacesDir, "index.ts");
|
|
@@ -6872,6 +7309,28 @@ function generateServiceBarrels(spec, ctx) {
|
|
|
6872
7309
|
} catch {}
|
|
6873
7310
|
}
|
|
6874
7311
|
}
|
|
7312
|
+
const ownedScanRoot = ctx.targetDir ?? ctx.outputDir;
|
|
7313
|
+
if (ownedScanRoot && isDirOwned) {
|
|
7314
|
+
const interfacesDir = path.join(ownedScanRoot, "src", dirName, "interfaces");
|
|
7315
|
+
const symbols = dirSymbols.get(dirName) ?? /* @__PURE__ */ new Set();
|
|
7316
|
+
try {
|
|
7317
|
+
for (const entry of fs.readdirSync(interfacesDir)) {
|
|
7318
|
+
if (entry === "index.ts") continue;
|
|
7319
|
+
if (!entry.endsWith(".ts")) continue;
|
|
7320
|
+
const exportLine = `export * from './${entry.replace(/\.ts$/, "")}';`;
|
|
7321
|
+
if (exportSet.has(exportLine)) continue;
|
|
7322
|
+
const content = fs.readFileSync(path.join(interfacesDir, entry), "utf-8");
|
|
7323
|
+
const exportedNames = [];
|
|
7324
|
+
for (const m of content.matchAll(/export\s+(?:interface|type|enum|class|const|function)\s+(\w+)/g)) exportedNames.push(m[1]);
|
|
7325
|
+
if (exportedNames.some((name) => globalExistingSymbols.has(name))) continue;
|
|
7326
|
+
for (const name of exportedNames) {
|
|
7327
|
+
symbols.add(name);
|
|
7328
|
+
globalExistingSymbols.add(name);
|
|
7329
|
+
}
|
|
7330
|
+
exportSet.add(exportLine);
|
|
7331
|
+
}
|
|
7332
|
+
} catch {}
|
|
7333
|
+
}
|
|
6875
7334
|
const uniqueExports = [...exportSet];
|
|
6876
7335
|
uniqueExports.sort();
|
|
6877
7336
|
if (ctx.apiSurface) files.push({
|
|
@@ -7088,6 +7547,9 @@ function serverConstName(description) {
|
|
|
7088
7547
|
}
|
|
7089
7548
|
//#endregion
|
|
7090
7549
|
//#region src/node/tests.ts
|
|
7550
|
+
function operationOverrideFor(ctx, op) {
|
|
7551
|
+
return nodeOptions(ctx).operationOverrides?.[`${op.httpMethod.toUpperCase()} ${op.path}`];
|
|
7552
|
+
}
|
|
7091
7553
|
function baselineMethodFor(service, method, ctx) {
|
|
7092
7554
|
const serviceClass = resolveResourceClassName$3(service, ctx);
|
|
7093
7555
|
return ctx.apiSurface?.classes?.[serviceClass]?.methods?.[method]?.[0];
|
|
@@ -7103,6 +7565,34 @@ function optionsObjectParam(method) {
|
|
|
7103
7565
|
type: param.type
|
|
7104
7566
|
};
|
|
7105
7567
|
}
|
|
7568
|
+
function configuredOptionsMethod(ctx, op) {
|
|
7569
|
+
const optionsType = operationOverrideFor(ctx, op)?.optionsType;
|
|
7570
|
+
if (!optionsType) return void 0;
|
|
7571
|
+
return { params: [{
|
|
7572
|
+
name: "options",
|
|
7573
|
+
type: optionsType,
|
|
7574
|
+
passingStyle: "options_object"
|
|
7575
|
+
}] };
|
|
7576
|
+
}
|
|
7577
|
+
function methodOptionsName(method, serviceClass) {
|
|
7578
|
+
if (method === "list") return `${toPascalCase(serviceClass)}ListOptions`;
|
|
7579
|
+
return `${toPascalCase(method)}Options`;
|
|
7580
|
+
}
|
|
7581
|
+
function hasOptionsInput(op, plan) {
|
|
7582
|
+
return op.pathParams.length > 0 || plan.hasBody || plan.isPaginated || op.queryParams.length > 0;
|
|
7583
|
+
}
|
|
7584
|
+
function optionsMethodFor(service, method, op, plan, ctx) {
|
|
7585
|
+
const baseline = baselineMethodFor(service, method, ctx);
|
|
7586
|
+
if (optionsObjectParam(baseline)) return baseline;
|
|
7587
|
+
const configured = configuredOptionsMethod(ctx, op);
|
|
7588
|
+
if (configured) return configured;
|
|
7589
|
+
if (!hasOptionsInput(op, plan)) return baseline;
|
|
7590
|
+
return { params: [{
|
|
7591
|
+
name: "options",
|
|
7592
|
+
type: methodOptionsName(method, resolveResourceClassName$3(service, ctx)),
|
|
7593
|
+
passingStyle: "options_object"
|
|
7594
|
+
}] };
|
|
7595
|
+
}
|
|
7106
7596
|
function autoPaginatableItemType(returnType) {
|
|
7107
7597
|
return returnType?.match(/\bAutoPaginatable<\s*([A-Za-z_$][\w$]*)/)?.[1];
|
|
7108
7598
|
}
|
|
@@ -7212,7 +7702,7 @@ function generateServiceTest$3(service, spec, ctx, modelMap, mountAccessors) {
|
|
|
7212
7702
|
lines.push(`describe('${serviceClass}', () => {`);
|
|
7213
7703
|
lines.push(" beforeEach(() => fetch.resetMocks());");
|
|
7214
7704
|
for (const { op, plan, method } of plans) {
|
|
7215
|
-
const existingMethod =
|
|
7705
|
+
const existingMethod = optionsMethodFor(service, method, op, plan, ctx);
|
|
7216
7706
|
lines.push("");
|
|
7217
7707
|
lines.push(` describe('${method}', () => {`);
|
|
7218
7708
|
if (plan.isPaginated) renderPaginatedTest(lines, op, plan, method, serviceProp, modelMap, ctx, entityHelperNames, existingMethod);
|
|
@@ -7329,6 +7819,12 @@ function renderBodyTest(lines, op, plan, method, serviceProp, modelMap, ctx, ent
|
|
|
7329
7819
|
if (payload) lines.push(` expect(fetchBody()).toEqual(expect.objectContaining(${payload.snakeCaseObj}));`);
|
|
7330
7820
|
else lines.push(" expect(fetchBody()).toBeDefined();");
|
|
7331
7821
|
if (isArrayResponse) lines.push(" expect(Array.isArray(result)).toBe(true);");
|
|
7822
|
+
const override = ctx ? operationOverrideFor(ctx, op) : void 0;
|
|
7823
|
+
if (override?.returnExpression || override?.returnDataProperty) {
|
|
7824
|
+
lines.push(" expect(result).toBeDefined();");
|
|
7825
|
+
lines.push(" });");
|
|
7826
|
+
return;
|
|
7827
|
+
}
|
|
7332
7828
|
const bodyHelperName = ctx ? `expect${resolveInterfaceName(responseModelName, ctx)}` : null;
|
|
7333
7829
|
if (bodyHelperName && entityHelpers?.has(bodyHelperName)) lines.push(` ${bodyHelperName}(${accessor});`);
|
|
7334
7830
|
else {
|
|
@@ -7358,6 +7854,13 @@ function renderGetTest(lines, op, plan, method, serviceProp, modelMap, ctx, enti
|
|
|
7358
7854
|
const expectedPathGet = buildExpectedPath$4(op);
|
|
7359
7855
|
lines.push(` expect(new URL(String(fetchURL())).pathname).toBe('${expectedPathGet}');`);
|
|
7360
7856
|
if (isArrayResponse) lines.push(" expect(Array.isArray(result)).toBe(true);");
|
|
7857
|
+
const returnDataProperty = ctx ? operationOverrideFor(ctx, op)?.returnDataProperty : void 0;
|
|
7858
|
+
if (returnDataProperty) {
|
|
7859
|
+
if ((modelMap.get(responseModelName)?.fields.find((field) => fieldName$6(field.name) === returnDataProperty))?.type.kind === "array") lines.push(" expect(Array.isArray(result)).toBe(true);");
|
|
7860
|
+
else lines.push(" expect(result).toBeDefined();");
|
|
7861
|
+
lines.push(" });");
|
|
7862
|
+
return;
|
|
7863
|
+
}
|
|
7361
7864
|
const helperName = ctx ? `expect${resolveInterfaceName(responseModelName, ctx)}` : null;
|
|
7362
7865
|
if (helperName && entityHelpers?.has(helperName)) lines.push(` ${helperName}(${accessor});`);
|
|
7363
7866
|
else {
|
|
@@ -7395,12 +7898,37 @@ function buildOptionsObjectTestArg(op, plan, baselineMethod, modelMap, ctx) {
|
|
|
7395
7898
|
const optionField = resolveOptionsObjectField(localName, optionParam.type, ctx);
|
|
7396
7899
|
entries.push(`${optionField}: ${JSON.stringify(pathParamTestValue(param, localName))}`);
|
|
7397
7900
|
}
|
|
7901
|
+
if (plan.isPaginated) entries.push("order: 'desc'");
|
|
7902
|
+
const queryParams = plan.isPaginated ? op.queryParams.filter((param) => ![
|
|
7903
|
+
"limit",
|
|
7904
|
+
"before",
|
|
7905
|
+
"after",
|
|
7906
|
+
"order"
|
|
7907
|
+
].includes(param.name)) : op.queryParams;
|
|
7908
|
+
for (const param of queryParams) {
|
|
7909
|
+
const localName = fieldName$6(param.name);
|
|
7910
|
+
const value = fixtureValueForType(param.type, param.name, "Options", modelMap) ?? "'test'";
|
|
7911
|
+
entries.push(`${localName}: ${value}`);
|
|
7912
|
+
}
|
|
7398
7913
|
if (plan.hasBody) {
|
|
7399
7914
|
const payload = buildTestPayload(op, modelMap);
|
|
7400
|
-
if (payload)
|
|
7915
|
+
if (payload) {
|
|
7916
|
+
const bodyFieldMap = ctx ? operationOverrideFor(ctx, op)?.bodyFieldMap : void 0;
|
|
7917
|
+
entries.push(...mapBodyOptionEntries(objectLiteralEntries(payload.camelCaseObj), bodyFieldMap));
|
|
7918
|
+
} else if (entries.length === 0) return "({} as any)";
|
|
7401
7919
|
}
|
|
7402
7920
|
return `{ ${entries.join(", ")} }`;
|
|
7403
7921
|
}
|
|
7922
|
+
function mapBodyOptionEntries(entries, bodyFieldMap) {
|
|
7923
|
+
const reverseMap = new Map(Object.entries(bodyFieldMap ?? {}).map(([source, target]) => [target, source]));
|
|
7924
|
+
if (reverseMap.size === 0) return entries;
|
|
7925
|
+
return entries.map((entry) => {
|
|
7926
|
+
const match = entry.match(/^([A-Za-z_$][\w$]*)\s*:/);
|
|
7927
|
+
if (!match) return entry;
|
|
7928
|
+
const source = reverseMap.get(match[1]);
|
|
7929
|
+
return source ? entry.replace(match[1], source) : entry;
|
|
7930
|
+
});
|
|
7931
|
+
}
|
|
7404
7932
|
function objectLiteralEntries(literal) {
|
|
7405
7933
|
const trimmed = literal.trim();
|
|
7406
7934
|
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return [];
|
|
@@ -7476,7 +8004,8 @@ function buildFieldAssertions(model, accessor, modelMap) {
|
|
|
7476
8004
|
for (const field of model.fields) {
|
|
7477
8005
|
if (!field.required) continue;
|
|
7478
8006
|
const domainField = fieldName$6(field.name);
|
|
7479
|
-
const
|
|
8007
|
+
const isDateTime = isDateTimeFieldType(field.type);
|
|
8008
|
+
const fieldAccessor = isDateTime ? `${accessor}.${domainField}.toISOString()` : `${accessor}.${domainField}`;
|
|
7480
8009
|
if (field.example !== void 0) {
|
|
7481
8010
|
if (field.example === null) {
|
|
7482
8011
|
assertions.push(`expect(${accessor}.${domainField}).toBeNull();`);
|
|
@@ -7484,7 +8013,7 @@ function buildFieldAssertions(model, accessor, modelMap) {
|
|
|
7484
8013
|
}
|
|
7485
8014
|
if (typeof field.example === "object") assertions.push(`expect(${accessor}.${domainField}).toEqual(${JSON.stringify(field.example)});`);
|
|
7486
8015
|
else {
|
|
7487
|
-
const exampleLiteral = typeof field.example === "string" ? `'${field.example}'` : String(field.example);
|
|
8016
|
+
const exampleLiteral = isDateTime && typeof field.example === "string" ? `'${new Date(field.example).toISOString()}'` : typeof field.example === "string" ? `'${field.example}'` : String(field.example);
|
|
7488
8017
|
assertions.push(`expect(${fieldAccessor}).toBe(${exampleLiteral});`);
|
|
7489
8018
|
}
|
|
7490
8019
|
continue;
|
|
@@ -7513,7 +8042,7 @@ function buildFieldAssertions(model, accessor, modelMap) {
|
|
|
7513
8042
|
*/
|
|
7514
8043
|
function fixtureValueForType(ref, name, modelName, modelMap, wire) {
|
|
7515
8044
|
switch (ref.kind) {
|
|
7516
|
-
case "primitive": return fixtureValueForPrimitive(ref.type, ref.format, name, modelName);
|
|
8045
|
+
case "primitive": return fixtureValueForPrimitive(ref.type, ref.format, name, modelName, wire);
|
|
7517
8046
|
case "literal": return typeof ref.value === "string" ? `'${ref.value}'` : String(ref.value);
|
|
7518
8047
|
case "enum":
|
|
7519
8048
|
if (ref.values?.length) {
|
|
@@ -7543,10 +8072,10 @@ function fixtureValueForType(ref, name, modelName, modelMap, wire) {
|
|
|
7543
8072
|
default: return null;
|
|
7544
8073
|
}
|
|
7545
8074
|
}
|
|
7546
|
-
function fixtureValueForPrimitive(type, format, name, modelName) {
|
|
8075
|
+
function fixtureValueForPrimitive(type, format, name, modelName, wire) {
|
|
7547
8076
|
switch (type) {
|
|
7548
8077
|
case "string":
|
|
7549
|
-
if (format === "date-time") return "'2023-01-01T00:00:00.000Z'";
|
|
8078
|
+
if (format === "date-time") return wire ? "'2023-01-01T00:00:00.000Z'" : "new Date('2023-01-01T00:00:00.000Z')";
|
|
7550
8079
|
if (format === "date") return "'2023-01-01'";
|
|
7551
8080
|
if (format === "uuid") return "'00000000-0000-0000-0000-000000000000'";
|
|
7552
8081
|
if (name === "id") return `'${ID_PREFIXES$4[modelName] ?? ""}01234'`;
|
|
@@ -7771,11 +8300,14 @@ function detectDiscriminatedShape(modelName, rawSchemas) {
|
|
|
7771
8300
|
};
|
|
7772
8301
|
}).filter((v) => v !== null);
|
|
7773
8302
|
if (variants.length !== flattenedVariants.length) return null;
|
|
8303
|
+
const baseFields = baseObject ? collectObjectFields(baseObject, modelName) : [];
|
|
8304
|
+
const discriminatorDescription = flattenedVariants[0].alwaysProperties.get(discProp)?.description;
|
|
7774
8305
|
return {
|
|
7775
8306
|
modelName,
|
|
7776
|
-
baseFields
|
|
8307
|
+
baseFields,
|
|
7777
8308
|
discriminatorProperty: discProp,
|
|
7778
8309
|
discriminatorPropertyDomain: toCamelCase(discProp),
|
|
8310
|
+
discriminatorDescription,
|
|
7779
8311
|
variants
|
|
7780
8312
|
};
|
|
7781
8313
|
}
|
|
@@ -7999,13 +8531,29 @@ function planDiscriminatedModels(models, ctx) {
|
|
|
7999
8531
|
if (!spec?.components?.schemas) return plans;
|
|
8000
8532
|
const rawSchemas = spec.components.schemas;
|
|
8001
8533
|
const { modelToService, resolveDir } = createServiceDirResolver(models, ctx.spec.services, ctx);
|
|
8534
|
+
const irModelDir = /* @__PURE__ */ new Map();
|
|
8535
|
+
for (const model of models) irModelDir.set(model.name, resolveDir(modelToService.get(model.name)));
|
|
8536
|
+
const depDirMap = /* @__PURE__ */ new Map();
|
|
8537
|
+
for (const rawName of Object.keys(rawSchemas)) {
|
|
8538
|
+
if (irModelDir.has(rawName)) {
|
|
8539
|
+
depDirMap.set(rawName, irModelDir.get(rawName));
|
|
8540
|
+
continue;
|
|
8541
|
+
}
|
|
8542
|
+
const stripped = rawName.replace(/Dto/g, "").replace(/DTO/g, "").replace(/Json$/, "");
|
|
8543
|
+
if (stripped !== rawName && irModelDir.has(stripped)) depDirMap.set(rawName, irModelDir.get(stripped));
|
|
8544
|
+
}
|
|
8002
8545
|
for (const model of models) {
|
|
8003
8546
|
const shape = detectDiscriminatedShape(model.name, rawSchemas);
|
|
8004
8547
|
if (!shape) continue;
|
|
8548
|
+
const allDeps = /* @__PURE__ */ new Set();
|
|
8549
|
+
for (const field of shape.baseFields) for (const d of field.modelDeps) allDeps.add(d);
|
|
8550
|
+
for (const variant of shape.variants) for (const field of variant.fields) for (const d of field.modelDeps) allDeps.add(d);
|
|
8551
|
+
if ([...allDeps].some((dep) => !depDirMap.has(dep) && !irModelDir.has(dep))) continue;
|
|
8005
8552
|
const modelDir = resolveDir(modelToService.get(model.name));
|
|
8006
8553
|
plans.set(model.name, {
|
|
8007
8554
|
shape,
|
|
8008
|
-
modelDir
|
|
8555
|
+
modelDir,
|
|
8556
|
+
depDirMap
|
|
8009
8557
|
});
|
|
8010
8558
|
}
|
|
8011
8559
|
return plans;
|
|
@@ -8050,8 +8598,14 @@ function buildInterfaceFile(plan, _ctx) {
|
|
|
8050
8598
|
function buildInterfaceBody(name, shape, variant, isWire) {
|
|
8051
8599
|
const lines = [];
|
|
8052
8600
|
lines.push(`export interface ${name} {`);
|
|
8053
|
-
|
|
8601
|
+
const variantFieldNames = new Set(variant.fields.map((f) => f.name));
|
|
8602
|
+
for (const field of shape.baseFields) {
|
|
8603
|
+
if (variantFieldNames.has(field.name)) continue;
|
|
8604
|
+
if (field.name === shape.discriminatorProperty) continue;
|
|
8605
|
+
pushFieldLine(lines, field, isWire);
|
|
8606
|
+
}
|
|
8054
8607
|
const discKey = isWire ? shape.discriminatorProperty : shape.discriminatorPropertyDomain;
|
|
8608
|
+
if (shape.discriminatorDescription) lines.push(` /** ${shape.discriminatorDescription} */`);
|
|
8055
8609
|
lines.push(` ${discKey}: '${variant.discriminatorValue}';`);
|
|
8056
8610
|
for (const field of variant.fields) pushFieldLine(lines, field, isWire);
|
|
8057
8611
|
lines.push("}");
|
|
@@ -8068,25 +8622,24 @@ function collectImports$2(plan) {
|
|
|
8068
8622
|
const deps = /* @__PURE__ */ new Set();
|
|
8069
8623
|
for (const field of plan.shape.baseFields) for (const d of field.modelDeps) deps.add(d);
|
|
8070
8624
|
for (const variant of plan.shape.variants) for (const field of variant.fields) for (const d of field.modelDeps) deps.add(d);
|
|
8071
|
-
const
|
|
8625
|
+
const result = [];
|
|
8072
8626
|
for (const dep of [...deps].sort()) {
|
|
8073
8627
|
const domain = toPascalCase(dep);
|
|
8074
|
-
symbols.push(domain);
|
|
8075
8628
|
const wire = wireInterfaceName(domain);
|
|
8076
|
-
|
|
8629
|
+
const symbols = wire !== domain ? [domain, wire] : [domain];
|
|
8630
|
+
const depDir = plan.depDirMap.get(dep);
|
|
8631
|
+
const baseName = fileName$3(toSnakeFromPascal(domain));
|
|
8632
|
+
let importPath;
|
|
8633
|
+
if (!depDir || depDir === plan.modelDir) importPath = `./${baseName}.interface`;
|
|
8634
|
+
else importPath = `../../${depDir}/interfaces/${baseName}.interface`;
|
|
8635
|
+
const existing = result.find((a) => a.path === importPath);
|
|
8636
|
+
if (existing) existing.symbols.push(...symbols);
|
|
8637
|
+
else result.push({
|
|
8638
|
+
path: importPath,
|
|
8639
|
+
symbols
|
|
8640
|
+
});
|
|
8077
8641
|
}
|
|
8078
|
-
|
|
8079
|
-
return symbols.map((sym) => {
|
|
8080
|
-
return {
|
|
8081
|
-
path: `./${fileName$3(toSnakeFromPascal(sym.replace(/Response$/, "")))}.interface`,
|
|
8082
|
-
symbols: [sym]
|
|
8083
|
-
};
|
|
8084
|
-
}).reduce((acc, cur) => {
|
|
8085
|
-
const existing = acc.find((a) => a.path === cur.path);
|
|
8086
|
-
if (existing) existing.symbols.push(...cur.symbols);
|
|
8087
|
-
else acc.push(cur);
|
|
8088
|
-
return acc;
|
|
8089
|
-
}, []);
|
|
8642
|
+
return result;
|
|
8090
8643
|
}
|
|
8091
8644
|
function toSnakeFromPascal(s) {
|
|
8092
8645
|
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toLowerCase();
|
|
@@ -8180,20 +8733,6 @@ function scalarModelDepName(field) {
|
|
|
8180
8733
|
}
|
|
8181
8734
|
//#endregion
|
|
8182
8735
|
//#region src/node/node-overrides.ts
|
|
8183
|
-
const OPERATION_OVERRIDES = {
|
|
8184
|
-
"POST /organizations/{organizationId}/groups": { methodName: "create_group" },
|
|
8185
|
-
"GET /organizations/{organizationId}/groups": { methodName: "list_groups" },
|
|
8186
|
-
"GET /organizations/{organizationId}/groups/{groupId}": { methodName: "get_group" },
|
|
8187
|
-
"PATCH /organizations/{organizationId}/groups/{groupId}": { methodName: "update_group" },
|
|
8188
|
-
"DELETE /organizations/{organizationId}/groups/{groupId}": { methodName: "delete_group" },
|
|
8189
|
-
"POST /organizations/{organizationId}/groups/{groupId}/organization-memberships": { methodName: "add_organization_membership" },
|
|
8190
|
-
"GET /organizations/{organizationId}/groups/{groupId}/organization-memberships": { methodName: "list_organization_memberships" },
|
|
8191
|
-
"DELETE /organizations/{organizationId}/groups/{groupId}/organization-memberships/{omId}": { methodName: "remove_organization_membership" },
|
|
8192
|
-
"GET /user_management/organization_memberships/{omId}/groups": {
|
|
8193
|
-
methodName: "list_groups_for_organization_membership",
|
|
8194
|
-
mountOn: "UserManagement"
|
|
8195
|
-
}
|
|
8196
|
-
};
|
|
8197
8736
|
const contextCache = /* @__PURE__ */ new WeakMap();
|
|
8198
8737
|
function operationKey(resolved) {
|
|
8199
8738
|
return `${resolved.operation.httpMethod.toUpperCase()} ${resolved.operation.path}`;
|
|
@@ -8245,9 +8784,10 @@ function withNodeOperationOverrides(ctx) {
|
|
|
8245
8784
|
contextCache.set(ctx, next);
|
|
8246
8785
|
return next;
|
|
8247
8786
|
}
|
|
8787
|
+
const configOverrides = nodeOptions(ctx).operationOverrides ?? {};
|
|
8248
8788
|
let opsChanged = false;
|
|
8249
8789
|
const nextResolved = resolvedOperations.map((resolved) => {
|
|
8250
|
-
const override =
|
|
8790
|
+
const override = configOverrides[operationKey(resolved)];
|
|
8251
8791
|
if (!override) return resolved;
|
|
8252
8792
|
const methodName = override.methodName ?? resolved.methodName;
|
|
8253
8793
|
const mountOn = override.mountOn ?? resolved.mountOn;
|
|
@@ -8475,20 +9015,48 @@ function isOwnedPath(relPath, policy) {
|
|
|
8475
9015
|
const dir = topLevelDir(relPath);
|
|
8476
9016
|
return dir !== void 0 && policy.ownedServiceDirs.has(dir);
|
|
8477
9017
|
}
|
|
9018
|
+
function extractRelativeImportPaths(content, fromPath) {
|
|
9019
|
+
const dir = path$1.dirname(fromPath);
|
|
9020
|
+
const paths = [];
|
|
9021
|
+
const re = /from\s+['"](\.[^'"]+)['"]/g;
|
|
9022
|
+
let match;
|
|
9023
|
+
while ((match = re.exec(content)) !== null) paths.push(path$1.normalize(path$1.join(dir, match[1])) + ".ts");
|
|
9024
|
+
return paths;
|
|
9025
|
+
}
|
|
8478
9026
|
function applyLiveSurface(files, ctx, surface) {
|
|
8479
9027
|
const out = [];
|
|
8480
9028
|
const policy = buildLiveSurfacePolicy(ctx, surface);
|
|
9029
|
+
const filesByPath = new Map(files.map((f) => [f.path, f]));
|
|
9030
|
+
const dependencyAllowedPaths = /* @__PURE__ */ new Set();
|
|
9031
|
+
const queue = [];
|
|
9032
|
+
for (const f of files) {
|
|
9033
|
+
if (f.integrateTarget === false) continue;
|
|
9034
|
+
if (!canCreateNewPath(f.path, policy)) continue;
|
|
9035
|
+
for (const importPath of extractRelativeImportPaths(f.content, f.path)) if (filesByPath.has(importPath) && !canCreateNewPath(importPath, policy) && !dependencyAllowedPaths.has(importPath)) {
|
|
9036
|
+
dependencyAllowedPaths.add(importPath);
|
|
9037
|
+
queue.push(importPath);
|
|
9038
|
+
}
|
|
9039
|
+
}
|
|
9040
|
+
while (queue.length > 0) {
|
|
9041
|
+
const relPath = queue.pop();
|
|
9042
|
+
const file = filesByPath.get(relPath);
|
|
9043
|
+
if (!file) continue;
|
|
9044
|
+
for (const importPath of extractRelativeImportPaths(file.content, relPath)) if (filesByPath.has(importPath) && !canCreateNewPath(importPath, policy) && !dependencyAllowedPaths.has(importPath)) {
|
|
9045
|
+
dependencyAllowedPaths.add(importPath);
|
|
9046
|
+
queue.push(importPath);
|
|
9047
|
+
}
|
|
9048
|
+
}
|
|
8481
9049
|
for (const f of files) {
|
|
8482
9050
|
const ownedPath = isOwnedPath(f.path, policy);
|
|
8483
9051
|
if (f.integrateTarget === false) continue;
|
|
8484
9052
|
if (surface.protectedFiles.has(f.path)) continue;
|
|
8485
|
-
if (policy.hasExistingSdk && !policy.managedPaths.has(f.path) && !canCreateNewPath(f.path, policy)) continue;
|
|
9053
|
+
if (policy.hasExistingSdk && !policy.managedPaths.has(f.path) && !canCreateNewPath(f.path, policy) && !dependencyAllowedPaths.has(f.path)) continue;
|
|
8486
9054
|
if (surface.files.has(f.path) && !surface.autogenFiles.has(f.path) && !ownedPath) continue;
|
|
8487
9055
|
if (isUserOwnedAfterFirstEmit(f.path)) {
|
|
8488
9056
|
const dir = topLevelDir(f.path);
|
|
8489
9057
|
const isAdoptedDir = dir !== void 0 && policy.adoptedServiceDirs.has(dir);
|
|
8490
9058
|
const isManagedDir = ownedPath || isAdoptedDir;
|
|
8491
|
-
if (surface.files.has(f.path) && !surface.autogenFiles.has(f.path)) continue;
|
|
9059
|
+
if (surface.files.has(f.path) && !surface.autogenFiles.has(f.path) && !(ownedPath && policy.regenerateOwnedTests)) continue;
|
|
8492
9060
|
if (!isManagedDir && !surface.autogenFiles.has(f.path)) continue;
|
|
8493
9061
|
if (isManagedDir && !policy.regenerateOwnedTests) continue;
|
|
8494
9062
|
}
|
|
@@ -8582,7 +9150,9 @@ const nodeEmitter = {
|
|
|
8582
9150
|
const surface = getSurface(nodeCtx);
|
|
8583
9151
|
const enriched = enrichModelsForNode(models);
|
|
8584
9152
|
const discPlans = planDiscriminatedModels(enriched, nodeCtx);
|
|
8585
|
-
|
|
9153
|
+
const discriminatedNames = new Set(discPlans.keys());
|
|
9154
|
+
nodeCtx._discriminatedModelNames = discriminatedNames;
|
|
9155
|
+
setDiscriminatedModelNames(discriminatedNames);
|
|
8586
9156
|
const standardFiles = generateModelsAndSerializers(enriched, nodeCtx);
|
|
8587
9157
|
const discFiles = generateDiscriminatedFiles(discPlans, nodeCtx);
|
|
8588
9158
|
return applyLiveSurface([...standardFiles, ...discFiles], nodeCtx, surface);
|
|
@@ -9195,11 +9765,24 @@ function generateEnums$6(enums, ctx) {
|
|
|
9195
9765
|
const canonicalCls = className$5(canonicalName);
|
|
9196
9766
|
const aliasCls = className$5(enumDef.name);
|
|
9197
9767
|
const lines = [];
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9768
|
+
if (canonicalDir === dirName) {
|
|
9769
|
+
lines.push("from typing import TypeAlias");
|
|
9770
|
+
lines.push(`from .${fileName$2(canonicalName)} import ${canonicalCls}`);
|
|
9771
|
+
lines.push("");
|
|
9772
|
+
lines.push(`${aliasCls}: TypeAlias = ${canonicalCls}`);
|
|
9773
|
+
} else {
|
|
9774
|
+
const modPath = `${ctx.namespace}.${dirToModule(canonicalDir)}.models.${fileName$2(canonicalName)}`;
|
|
9775
|
+
lines.push("from typing import TYPE_CHECKING");
|
|
9776
|
+
lines.push("");
|
|
9777
|
+
lines.push("if TYPE_CHECKING:");
|
|
9778
|
+
lines.push(` from ${modPath} import ${canonicalCls} as ${aliasCls}`);
|
|
9779
|
+
lines.push("else:");
|
|
9780
|
+
lines.push(" def __getattr__(name: str):");
|
|
9781
|
+
lines.push(` if name == "${aliasCls}":`);
|
|
9782
|
+
lines.push(` from ${modPath} import ${canonicalCls}`);
|
|
9783
|
+
lines.push(` return ${canonicalCls}`);
|
|
9784
|
+
lines.push(" raise AttributeError(f\"module {__name__!r} has no attribute {name!r}\")");
|
|
9785
|
+
}
|
|
9203
9786
|
lines.push(`__all__ = ["${aliasCls}"]`);
|
|
9204
9787
|
files.push({
|
|
9205
9788
|
path: `src/${ctx.namespace}/${dirName}/models/${fileName$2(enumDef.name)}.py`,
|
|
@@ -9208,16 +9791,33 @@ function generateEnums$6(enums, ctx) {
|
|
|
9208
9791
|
overwriteExisting: true
|
|
9209
9792
|
});
|
|
9210
9793
|
for (const aliasName of compatAliases.get(enumDef.name) ?? []) {
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9794
|
+
let compatContent;
|
|
9795
|
+
if (canonicalDir === dirName) compatContent = [
|
|
9796
|
+
"from typing import TypeAlias",
|
|
9797
|
+
`from .${fileName$2(canonicalName)} import ${canonicalCls}`,
|
|
9798
|
+
"",
|
|
9799
|
+
`${aliasName}: TypeAlias = ${canonicalCls}`,
|
|
9800
|
+
`__all__ = ["${aliasName}"]`
|
|
9801
|
+
].join("\n");
|
|
9802
|
+
else {
|
|
9803
|
+
const modPath = `${ctx.namespace}.${dirToModule(canonicalDir)}.models.${fileName$2(canonicalName)}`;
|
|
9804
|
+
compatContent = [
|
|
9805
|
+
"from typing import TYPE_CHECKING",
|
|
9217
9806
|
"",
|
|
9218
|
-
|
|
9807
|
+
"if TYPE_CHECKING:",
|
|
9808
|
+
` from ${modPath} import ${canonicalCls} as ${aliasName}`,
|
|
9809
|
+
"else:",
|
|
9810
|
+
" def __getattr__(name: str):",
|
|
9811
|
+
` if name == "${aliasName}":`,
|
|
9812
|
+
` from ${modPath} import ${canonicalCls}`,
|
|
9813
|
+
` return ${canonicalCls}`,
|
|
9814
|
+
" raise AttributeError(f\"module {__name__!r} has no attribute {name!r}\")",
|
|
9219
9815
|
`__all__ = ["${aliasName}"]`
|
|
9220
|
-
].join("\n")
|
|
9816
|
+
].join("\n");
|
|
9817
|
+
}
|
|
9818
|
+
files.push({
|
|
9819
|
+
path: `src/${ctx.namespace}/${dirName}/models/${fileName$2(aliasName)}.py`,
|
|
9820
|
+
content: compatContent,
|
|
9221
9821
|
integrateTarget: true,
|
|
9222
9822
|
overwriteExisting: true
|
|
9223
9823
|
});
|
|
@@ -9391,6 +9991,8 @@ function generateModels$6(models, ctx) {
|
|
|
9391
9991
|
const emittedFilePaths = /* @__PURE__ */ new Set();
|
|
9392
9992
|
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
9393
9993
|
const listMetadataNeeded = collectReferencedListMetadataModels(models, nonPaginatedRefs);
|
|
9994
|
+
const discriminatorNames = /* @__PURE__ */ new Set();
|
|
9995
|
+
for (const m of models) if (m.discriminator) discriminatorNames.add(m.name);
|
|
9394
9996
|
for (const model of models) {
|
|
9395
9997
|
if (isListWrapperModel(model) && !nonPaginatedRefs.has(model.name)) continue;
|
|
9396
9998
|
if (isListMetadataModel(model) && !listMetadataNeeded.has(model.name)) continue;
|
|
@@ -9526,7 +10128,7 @@ function generateModels$6(models, ctx) {
|
|
|
9526
10128
|
typingImports.add("Dict");
|
|
9527
10129
|
for (const field of deduplicatedFields) collectTypingImports(field.type, typingImports);
|
|
9528
10130
|
if (deduplicatedFields.some((f) => isOptionalField(model.name, f, ctx))) typingImports.add("Optional");
|
|
9529
|
-
const usesDateTime = deduplicatedFields.some((f) => isDateTimeType(f.type));
|
|
10131
|
+
const usesDateTime = deduplicatedFields.some((f) => isDateTimeType$1(f.type));
|
|
9530
10132
|
const usesEnum = deps.enums.size > 0;
|
|
9531
10133
|
lines.push("from __future__ import annotations");
|
|
9532
10134
|
lines.push("");
|
|
@@ -9542,8 +10144,9 @@ function generateModels$6(models, ctx) {
|
|
|
9542
10144
|
for (const modelName of [...deps.models].sort()) {
|
|
9543
10145
|
if (modelName === model.name) continue;
|
|
9544
10146
|
const modelDir = resolveDir(modelToService.get(modelName));
|
|
9545
|
-
|
|
9546
|
-
|
|
10147
|
+
const importNames = discriminatorNames.has(modelName) ? `${className$5(modelName)}, ${className$5(modelName)}Variant` : className$5(modelName);
|
|
10148
|
+
if (modelDir === dirName) lines.push(`from .${fileName$2(modelName)} import ${importNames}`);
|
|
10149
|
+
else lines.push(`from ${ctx.namespace}.${dirToModule(modelDir)}.models.${fileName$2(modelName)} import ${importNames}`);
|
|
9547
10150
|
}
|
|
9548
10151
|
}
|
|
9549
10152
|
if (deps.enums.size > 0) for (const enumName of [...deps.enums].sort()) {
|
|
@@ -9564,9 +10167,16 @@ function generateModels$6(models, ctx) {
|
|
|
9564
10167
|
lines.push("");
|
|
9565
10168
|
const requiredFields = deduplicatedFields.filter((f) => !isOptionalField(model.name, f, ctx));
|
|
9566
10169
|
const optionalFields = deduplicatedFields.filter((f) => isOptionalField(model.name, f, ctx));
|
|
10170
|
+
const rewriteDiscriminatorType = (typeStr) => {
|
|
10171
|
+
for (const discName of discriminatorNames) {
|
|
10172
|
+
const quoted = `"${className$5(discName)}"`;
|
|
10173
|
+
if (typeStr.includes(quoted)) typeStr = typeStr.replace(quoted, `"${className$5(discName)}Variant"`);
|
|
10174
|
+
}
|
|
10175
|
+
return typeStr;
|
|
10176
|
+
};
|
|
9567
10177
|
for (const field of requiredFields) {
|
|
9568
10178
|
const pyFieldName = fieldName$5(field.name);
|
|
9569
|
-
const pyType = resolveModelFieldType(field.type);
|
|
10179
|
+
const pyType = rewriteDiscriminatorType(resolveModelFieldType(field.type));
|
|
9570
10180
|
if (field.description || field.deprecated) {
|
|
9571
10181
|
const parts = [];
|
|
9572
10182
|
if (field.description) parts.push(field.description);
|
|
@@ -9577,7 +10187,7 @@ function generateModels$6(models, ctx) {
|
|
|
9577
10187
|
}
|
|
9578
10188
|
for (const field of optionalFields) {
|
|
9579
10189
|
const pyFieldName = fieldName$5(field.name);
|
|
9580
|
-
const pyType = `Optional[${field.type.kind === "nullable" ? resolveModelFieldType(field.type.inner) : resolveModelFieldType(field.type)}]`;
|
|
10190
|
+
const pyType = `Optional[${rewriteDiscriminatorType(field.type.kind === "nullable" ? resolveModelFieldType(field.type.inner) : resolveModelFieldType(field.type))}]`;
|
|
9581
10191
|
if (field.description || field.deprecated) {
|
|
9582
10192
|
const parts = [];
|
|
9583
10193
|
if (field.description) parts.push(field.description);
|
|
@@ -9821,12 +10431,12 @@ function pythonLiteralDefault(value) {
|
|
|
9821
10431
|
return String(value);
|
|
9822
10432
|
}
|
|
9823
10433
|
function resolveModelFieldType(ref) {
|
|
9824
|
-
if (ref.kind === "nullable" && isDateTimeType(ref.inner)) return "Optional[datetime]";
|
|
9825
|
-
if (isDateTimeType(ref)) return "datetime";
|
|
10434
|
+
if (ref.kind === "nullable" && isDateTimeType$1(ref.inner)) return "Optional[datetime]";
|
|
10435
|
+
if (isDateTimeType$1(ref)) return "datetime";
|
|
9826
10436
|
return mapTypeRef$6(ref);
|
|
9827
10437
|
}
|
|
9828
|
-
function isDateTimeType(ref) {
|
|
9829
|
-
if (ref.kind === "nullable") return isDateTimeType(ref.inner);
|
|
10438
|
+
function isDateTimeType$1(ref) {
|
|
10439
|
+
if (ref.kind === "nullable") return isDateTimeType$1(ref.inner);
|
|
9830
10440
|
return ref.kind === "primitive" && ref.type === "string" && ref.format === "date-time";
|
|
9831
10441
|
}
|
|
9832
10442
|
/**
|
|
@@ -9887,7 +10497,7 @@ function renderDiscriminatedUnionPrelude(field, pyFieldName, wireKey, parentClas
|
|
|
9887
10497
|
prelude.push(`${indent}if ${rawVar} is None:`);
|
|
9888
10498
|
prelude.push(`${indent} ${valueVar} = None`);
|
|
9889
10499
|
prelude.push(`${indent}else:`);
|
|
9890
|
-
prelude.push(...dispatchBlock(
|
|
10500
|
+
prelude.push(...dispatchBlock(" "));
|
|
9891
10501
|
prelude.push(`${indent} ${valueVar} = ${clsVar}.from_dict(${dataVar})`);
|
|
9892
10502
|
return {
|
|
9893
10503
|
prelude,
|
|
@@ -9895,7 +10505,7 @@ function renderDiscriminatedUnionPrelude(field, pyFieldName, wireKey, parentClas
|
|
|
9895
10505
|
};
|
|
9896
10506
|
}
|
|
9897
10507
|
function deserializeField(ref, accessor, isRequired, walrusVar = "_v") {
|
|
9898
|
-
if (isDateTimeType(ref)) {
|
|
10508
|
+
if (isDateTimeType$1(ref)) {
|
|
9899
10509
|
if (isRequired) return `_parse_datetime(${accessor})`;
|
|
9900
10510
|
return `_parse_datetime(${walrusVar}) if (${walrusVar} := ${accessor}) is not None else None`;
|
|
9901
10511
|
}
|
|
@@ -9934,7 +10544,7 @@ function deserializeField(ref, accessor, isRequired, walrusVar = "_v") {
|
|
|
9934
10544
|
}
|
|
9935
10545
|
}
|
|
9936
10546
|
function serializeField(ref, accessor) {
|
|
9937
|
-
if (isDateTimeType(ref)) return `_format_datetime(${accessor})`;
|
|
10547
|
+
if (isDateTimeType$1(ref)) return `_format_datetime(${accessor})`;
|
|
9938
10548
|
switch (ref.kind) {
|
|
9939
10549
|
case "model": return `${accessor}.to_dict()`;
|
|
9940
10550
|
case "enum": return `${accessor}.value if isinstance(${accessor}, Enum) else ${accessor}`;
|
|
@@ -10078,7 +10688,8 @@ function emitWrapperMethod$5(lines, resolvedOp, wrapper, ctx, isAsync) {
|
|
|
10078
10688
|
else lines.push(` ${pyName}: ${pyType},`);
|
|
10079
10689
|
}
|
|
10080
10690
|
lines.push(" request_options: Optional[RequestOptions] = None,");
|
|
10081
|
-
const
|
|
10691
|
+
const isDiscriminatorResponse = wrapper.responseModelName ? !!ctx.spec.models.find((m) => m.name === wrapper.responseModelName)?.discriminator : false;
|
|
10692
|
+
const responseType = wrapper.responseModelName ? isDiscriminatorResponse ? className$5(wrapper.responseModelName) + "Variant" : className$5(wrapper.responseModelName) : "None";
|
|
10082
10693
|
lines.push(` ) -> ${responseType}:`);
|
|
10083
10694
|
lines.push(` """${formatWrapperDescription(wrapper.name)}."""`);
|
|
10084
10695
|
lines.push(" body: Dict[str, Any] = {");
|
|
@@ -10103,7 +10714,19 @@ function emitWrapperMethod$5(lines, resolvedOp, wrapper, ctx, isAsync) {
|
|
|
10103
10714
|
const pathExpr = buildPythonPathExpression(op.path);
|
|
10104
10715
|
const awaitPrefix = isAsync ? "await " : "";
|
|
10105
10716
|
lines.push("");
|
|
10106
|
-
if (wrapper.responseModelName) {
|
|
10717
|
+
if (wrapper.responseModelName && isDiscriminatorResponse) {
|
|
10718
|
+
const variantType = className$5(wrapper.responseModelName) + "Variant";
|
|
10719
|
+
lines.push(` return cast(`);
|
|
10720
|
+
lines.push(` ${variantType},`);
|
|
10721
|
+
lines.push(` ${awaitPrefix}self._client.request(`);
|
|
10722
|
+
lines.push(` method="${op.httpMethod.toUpperCase()}",`);
|
|
10723
|
+
lines.push(` path=${pathExpr},`);
|
|
10724
|
+
lines.push(" body=body,");
|
|
10725
|
+
lines.push(` model=${className$5(wrapper.responseModelName)}, # type: ignore[arg-type] # dispatcher; request only calls from_dict`);
|
|
10726
|
+
lines.push(" request_options=request_options,");
|
|
10727
|
+
lines.push(" )");
|
|
10728
|
+
lines.push(" )");
|
|
10729
|
+
} else if (wrapper.responseModelName) {
|
|
10107
10730
|
lines.push(` return ${awaitPrefix}self._client.request(`);
|
|
10108
10731
|
lines.push(` method="${op.httpMethod.toUpperCase()}",`);
|
|
10109
10732
|
lines.push(` path=${pathExpr},`);
|
|
@@ -10335,8 +10958,8 @@ function emitMethodSignature(lines, op, plan, method, isAsync, specEnumNames, mo
|
|
|
10335
10958
|
else if (isPaginated) {
|
|
10336
10959
|
const resolvedItem = resolvePageItemName(op.pagination.itemType, listWrapperNames, ctx);
|
|
10337
10960
|
returnType = `${pageType}[${ctx.spec.models.find((m) => m.name === resolvedItem)?.discriminator ? className$5(resolvedItem) + "Variant" : className$5(resolvedItem)}]`;
|
|
10338
|
-
} else if (isArrayResponse) returnType = `List[${className$5(plan.responseModelName)}]`;
|
|
10339
|
-
else if (plan.responseModelName) returnType = className$5(plan.responseModelName);
|
|
10961
|
+
} else if (isArrayResponse) returnType = `List[${ctx.spec.models.find((m) => m.name === plan.responseModelName)?.discriminator ? className$5(plan.responseModelName) + "Variant" : className$5(plan.responseModelName)}]`;
|
|
10962
|
+
else if (plan.responseModelName) returnType = ctx.spec.models.find((m) => m.name === plan.responseModelName)?.discriminator ? className$5(plan.responseModelName) + "Variant" : className$5(plan.responseModelName);
|
|
10340
10963
|
else returnType = "None";
|
|
10341
10964
|
lines.push(` ) -> ${returnType}:`);
|
|
10342
10965
|
return {
|
|
@@ -10627,6 +11250,7 @@ function emitMethodBody(lines, op, plan, meta, isAsync, modelImports, listWrappe
|
|
|
10627
11250
|
lines.push(" )");
|
|
10628
11251
|
} else if (plan.hasBody && op.requestBody) {
|
|
10629
11252
|
const responseModel = plan.responseModelName ? className$5(plan.responseModelName) : "None";
|
|
11253
|
+
const isBodyResponseDiscriminator = !!(plan.responseModelName ? ctx.spec.models.find((m) => m.name === plan.responseModelName) : null)?.discriminator;
|
|
10630
11254
|
const bodyGroupedParams = collectGroupedParamNames(op);
|
|
10631
11255
|
const bodyModel = ctx.spec.models.find((m) => op.requestBody?.kind === "model" && m.name === op.requestBody.name);
|
|
10632
11256
|
const bodyFieldNamesSet = /* @__PURE__ */ new Set();
|
|
@@ -10664,6 +11288,20 @@ function emitMethodBody(lines, op, plan, meta, isAsync, modelImports, listWrappe
|
|
|
10664
11288
|
lines.push(" request_options=request_options,");
|
|
10665
11289
|
lines.push(" )");
|
|
10666
11290
|
lines.push(` return [${itemModel}.from_dict(cast(Dict[str, Any], item)) for item in raw]`);
|
|
11291
|
+
} else if (isBodyResponseDiscriminator && responseModel !== "None") {
|
|
11292
|
+
const variantType = responseModel + "Variant";
|
|
11293
|
+
lines.push(` return cast(`);
|
|
11294
|
+
lines.push(` ${variantType},`);
|
|
11295
|
+
lines.push(` ${awaitPrefix}self._client.request(`);
|
|
11296
|
+
lines.push(` method="${httpMethod}",`);
|
|
11297
|
+
lines.push(` path=${pathStr},`);
|
|
11298
|
+
lines.push(` body=${bodyVarName},`);
|
|
11299
|
+
if (bodyHasParams) lines.push(" params=params,");
|
|
11300
|
+
lines.push(` model=${responseModel}, # type: ignore[arg-type] # dispatcher; request only calls from_dict`);
|
|
11301
|
+
if (plan.isIdempotentPost) lines.push(" idempotency_key=idempotency_key,");
|
|
11302
|
+
lines.push(" request_options=request_options,");
|
|
11303
|
+
lines.push(" )");
|
|
11304
|
+
lines.push(" )");
|
|
10667
11305
|
} else {
|
|
10668
11306
|
const bodyReturnPrefix = responseModel !== "None" ? "return " : "";
|
|
10669
11307
|
lines.push(` ${bodyReturnPrefix}${awaitPrefix}self._client.request(`);
|
|
@@ -10678,6 +11316,7 @@ function emitMethodBody(lines, op, plan, meta, isAsync, modelImports, listWrappe
|
|
|
10678
11316
|
}
|
|
10679
11317
|
} else {
|
|
10680
11318
|
const responseModel = plan.responseModelName ? className$5(plan.responseModelName) : "None";
|
|
11319
|
+
const isGetResponseDiscriminator = !!(plan.responseModelName ? ctx.spec.models.find((m) => m.name === plan.responseModelName) : null)?.discriminator;
|
|
10681
11320
|
const getGroupedParams = collectGroupedParamNames(op);
|
|
10682
11321
|
const hasGroups = (op.parameterGroups?.length ?? 0) > 0;
|
|
10683
11322
|
const visibleQueryParams = op.queryParams.filter((p) => !hiddenParams.has(p.name) && !getGroupedParams.has(p.name));
|
|
@@ -10720,6 +11359,18 @@ function emitMethodBody(lines, op, plan, meta, isAsync, modelImports, listWrappe
|
|
|
10720
11359
|
lines.push(" request_options=request_options,");
|
|
10721
11360
|
lines.push(" )");
|
|
10722
11361
|
lines.push(` return [${itemModel}.from_dict(cast(Dict[str, Any], item)) for item in raw]`);
|
|
11362
|
+
} else if (isGetResponseDiscriminator && responseModel !== "None") {
|
|
11363
|
+
const variantType = responseModel + "Variant";
|
|
11364
|
+
lines.push(` return cast(`);
|
|
11365
|
+
lines.push(` ${variantType},`);
|
|
11366
|
+
lines.push(` ${awaitPrefix}self._client.request(`);
|
|
11367
|
+
lines.push(` method="${httpMethod}",`);
|
|
11368
|
+
lines.push(` path=${pathStr},`);
|
|
11369
|
+
if (emittedParams) lines.push(" params=params,");
|
|
11370
|
+
lines.push(` model=${responseModel}, # type: ignore[arg-type] # dispatcher; request only calls from_dict`);
|
|
11371
|
+
lines.push(" request_options=request_options,");
|
|
11372
|
+
lines.push(" )");
|
|
11373
|
+
lines.push(" )");
|
|
10723
11374
|
} else {
|
|
10724
11375
|
const returnPrefix = responseModel !== "None" ? "return " : "";
|
|
10725
11376
|
lines.push(` ${returnPrefix}${awaitPrefix}self._client.request(`);
|
|
@@ -10808,6 +11459,9 @@ function generateResources$6(services, ctx) {
|
|
|
10808
11459
|
const plan = planOperation(op);
|
|
10809
11460
|
if (plan.responseModelName) {
|
|
10810
11461
|
if (!listWrapperNames.has(plan.responseModelName) || !plan.isPaginated) modelImports.add(plan.responseModelName);
|
|
11462
|
+
if (!plan.isPaginated) {
|
|
11463
|
+
if (ctx.spec.models.find((m) => m.name === plan.responseModelName)?.discriminator) modelImports.add(plan.responseModelName + "Variant");
|
|
11464
|
+
}
|
|
10811
11465
|
}
|
|
10812
11466
|
if (op.requestBody?.kind === "model") {
|
|
10813
11467
|
const requestBodyRef = op.requestBody;
|
|
@@ -10867,10 +11521,15 @@ function generateResources$6(services, ctx) {
|
|
|
10867
11521
|
}
|
|
10868
11522
|
}
|
|
10869
11523
|
const localSet = new Set(localModels);
|
|
11524
|
+
const variantToFile = /* @__PURE__ */ new Map();
|
|
11525
|
+
for (const model of ctx.spec.models) if (model.discriminator) variantToFile.set(model.name + "Variant", fileName$2(model.name));
|
|
10870
11526
|
if (localModels.length > 0) lines.push(`from .models import ${localModels.map((n) => className$5(n)).join(", ")}`);
|
|
10871
11527
|
for (const [csDir, names] of [...crossServiceModels].sort()) {
|
|
10872
11528
|
const unique = names.filter((n) => !localSet.has(n));
|
|
10873
|
-
for (const n of unique)
|
|
11529
|
+
for (const n of unique) {
|
|
11530
|
+
const filePath = variantToFile.get(n) ?? fileName$2(n);
|
|
11531
|
+
lines.push(`from ${ctx.namespace}.${dirToModule(csDir)}.models.${filePath} import ${className$5(n)}`);
|
|
11532
|
+
}
|
|
10874
11533
|
}
|
|
10875
11534
|
const enumToServiceMap = placement.enumToService;
|
|
10876
11535
|
const localEnums = [];
|
|
@@ -11101,11 +11760,6 @@ const NON_SPEC_SERVICES = [
|
|
|
11101
11760
|
description: "Passwordless (magic-link) session endpoints, not yet in the OpenAPI spec.",
|
|
11102
11761
|
hasClientAccessor: true
|
|
11103
11762
|
},
|
|
11104
|
-
{
|
|
11105
|
-
id: "vault",
|
|
11106
|
-
description: "Vault KV storage, key operations, and client-side AES-GCM encrypt/decrypt.",
|
|
11107
|
-
hasClientAccessor: true
|
|
11108
|
-
},
|
|
11109
11763
|
{
|
|
11110
11764
|
id: "webhook_verification",
|
|
11111
11765
|
description: "Webhook signature verification and event deserialization (H01/H02)."
|
|
@@ -11134,14 +11788,6 @@ const PYTHON_NON_SPEC_WIRING = {
|
|
|
11134
11788
|
ctorArg: "self",
|
|
11135
11789
|
docstring: "Passwordless authentication sessions."
|
|
11136
11790
|
},
|
|
11137
|
-
vault: {
|
|
11138
|
-
importLine: "from .vault import AsyncVault, Vault",
|
|
11139
|
-
prop: "vault",
|
|
11140
|
-
syncClass: "Vault",
|
|
11141
|
-
asyncClass: "AsyncVault",
|
|
11142
|
-
ctorArg: "self",
|
|
11143
|
-
docstring: "Vault encryption, key management, and secret storage."
|
|
11144
|
-
},
|
|
11145
11791
|
actions: {
|
|
11146
11792
|
importLine: "from .actions import Actions, AsyncActions",
|
|
11147
11793
|
prop: "actions",
|
|
@@ -11469,16 +12115,14 @@ function generateModelFixture$4(model, modelMap, enumMap) {
|
|
|
11469
12115
|
});
|
|
11470
12116
|
for (const field of deduplicatedFields) {
|
|
11471
12117
|
const wireName = field.name;
|
|
11472
|
-
if (field.example !== void 0) fixture[wireName] = field.example;
|
|
12118
|
+
if (field.example !== void 0) fixture[wireName] = normalizeDateTimeExample(field, field.example);
|
|
11473
12119
|
else fixture[wireName] = generateFieldValue$3(field.type, field.name, model.name, modelMap, enumMap);
|
|
11474
12120
|
}
|
|
11475
12121
|
if (model.discriminator) {
|
|
11476
12122
|
const [firstValue, variantName] = Object.entries(model.discriminator.mapping)[0];
|
|
11477
12123
|
fixture[model.discriminator.property] = firstValue;
|
|
11478
12124
|
const variantModel = modelMap.get(variantName);
|
|
11479
|
-
if (variantModel)
|
|
11480
|
-
for (const field of variantModel.fields) if (!(field.name in fixture)) fixture[field.name] = field.example !== void 0 ? field.example : generateFieldValue$3(field.type, field.name, model.name, modelMap, enumMap);
|
|
11481
|
-
}
|
|
12125
|
+
if (variantModel) for (const field of variantModel.fields) fixture[field.name] = field.example !== void 0 ? normalizeDateTimeExample(field, field.example) : generateFieldValue$3(field.type, field.name, variantName, modelMap, enumMap);
|
|
11482
12126
|
}
|
|
11483
12127
|
return fixture;
|
|
11484
12128
|
}
|
|
@@ -11505,6 +12149,20 @@ function generateFieldValue$3(ref, fieldName, modelName, modelMap, enumMap) {
|
|
|
11505
12149
|
case "map": return { key: generateFieldValue$3(ref.valueType, "value", modelName, modelMap, enumMap) };
|
|
11506
12150
|
}
|
|
11507
12151
|
}
|
|
12152
|
+
/**
|
|
12153
|
+
* Normalize date-time example values to millisecond precision so that
|
|
12154
|
+
* round-trip tests (from_dict → to_dict) produce identical output.
|
|
12155
|
+
* Python's _format_datetime always serializes with milliseconds.
|
|
12156
|
+
*/
|
|
12157
|
+
function normalizeDateTimeExample(field, value) {
|
|
12158
|
+
if (typeof value !== "string") return value;
|
|
12159
|
+
const ref = field.type.kind === "nullable" ? field.type.inner : field.type;
|
|
12160
|
+
if (ref?.kind !== "primitive" || ref?.type !== "string" || ref?.format !== "date-time") return value;
|
|
12161
|
+
const match = value.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d+)?(Z|[+-]\d{2}:\d{2})$/);
|
|
12162
|
+
if (!match) return value;
|
|
12163
|
+
const [, prefix, , suffix] = match;
|
|
12164
|
+
return `${prefix}.000${suffix}`;
|
|
12165
|
+
}
|
|
11508
12166
|
function generatePrimitiveValue$3(type, format, name, modelName) {
|
|
11509
12167
|
switch (type) {
|
|
11510
12168
|
case "string":
|
|
@@ -11609,7 +12267,22 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
|
|
|
11609
12267
|
const enumImports = /* @__PURE__ */ new Set();
|
|
11610
12268
|
for (const op of service.operations) {
|
|
11611
12269
|
const plan = planOperation(op);
|
|
11612
|
-
if (plan.responseModelName)
|
|
12270
|
+
if (plan.responseModelName) {
|
|
12271
|
+
modelImports.add(plan.responseModelName);
|
|
12272
|
+
if (!plan.isPaginated) {
|
|
12273
|
+
const resolvedVariantClass = resolvePaginatedItemClass(plan.responseModelName, spec);
|
|
12274
|
+
if (resolvedVariantClass && resolvedVariantClass !== className$5(plan.responseModelName)) {
|
|
12275
|
+
const responseModel = spec.models.find((m) => m.name === plan.responseModelName);
|
|
12276
|
+
const disc = responseModel && responseModel.discriminator;
|
|
12277
|
+
if (disc) {
|
|
12278
|
+
for (const variantName of Object.values(disc.mapping)) if (className$5(variantName) === resolvedVariantClass) {
|
|
12279
|
+
modelImports.add(variantName);
|
|
12280
|
+
break;
|
|
12281
|
+
}
|
|
12282
|
+
}
|
|
12283
|
+
}
|
|
12284
|
+
}
|
|
12285
|
+
}
|
|
11613
12286
|
if (op.pagination?.itemType.kind === "model") {
|
|
11614
12287
|
modelImports.add(op.pagination.itemType.name);
|
|
11615
12288
|
let paginationItemName = op.pagination.itemType.name;
|
|
@@ -11652,10 +12325,12 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
|
|
|
11652
12325
|
if (param.type.kind === "nullable" && param.type.inner.kind === "enum") enumImports.add(param.type.inner.name);
|
|
11653
12326
|
}
|
|
11654
12327
|
}
|
|
12328
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(spec.services);
|
|
11655
12329
|
const actualImports = [...modelImports].filter((name) => {
|
|
11656
12330
|
const model = spec.models.find((m) => m.name === name);
|
|
11657
12331
|
if (!model) return true;
|
|
11658
|
-
|
|
12332
|
+
if (isListWrapperModel(model) && !nonPaginatedRefs.has(name)) return false;
|
|
12333
|
+
return true;
|
|
11659
12334
|
});
|
|
11660
12335
|
const placement = computeSchemaPlacement(spec, ctx);
|
|
11661
12336
|
const modelToServiceMap = placement.modelToService;
|
|
@@ -11794,14 +12469,22 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
|
|
|
11794
12469
|
const modelName = plan.responseModelName;
|
|
11795
12470
|
const fixtureName = `${fileName$2(modelName)}.json`;
|
|
11796
12471
|
const modelClass = className$5(modelName);
|
|
12472
|
+
const resolvedClass = resolvePaginatedItemClass(modelName, spec) ?? modelClass;
|
|
12473
|
+
const resolvedModelName = resolvedClass !== modelClass ? (() => {
|
|
12474
|
+
const disc = spec.models.find((m) => m.name === modelName)?.discriminator;
|
|
12475
|
+
if (disc) {
|
|
12476
|
+
for (const variantName of Object.values(disc.mapping)) if (className$5(variantName) === resolvedClass) return variantName;
|
|
12477
|
+
}
|
|
12478
|
+
return modelName;
|
|
12479
|
+
})() : modelName;
|
|
11797
12480
|
lines.push(` def test_${method}(self, workos, httpx_mock):`);
|
|
11798
12481
|
lines.push(` httpx_mock.add_response(`);
|
|
11799
12482
|
lines.push(` json=load_fixture("${fixtureName}"),`);
|
|
11800
12483
|
lines.push(" )");
|
|
11801
12484
|
const args = buildTestArgs$1(op, spec, hiddenParams);
|
|
11802
12485
|
lines.push(` result = workos.${propName}.${method}(${args})`);
|
|
11803
|
-
lines.push(` assert isinstance(result, ${
|
|
11804
|
-
const assertFields = pickAssertableFields(
|
|
12486
|
+
lines.push(` assert isinstance(result, ${resolvedClass})`);
|
|
12487
|
+
const assertFields = pickAssertableFields(resolvedModelName, spec);
|
|
11805
12488
|
for (const af of assertFields) {
|
|
11806
12489
|
const op_ = af.isBool ? "is" : "==";
|
|
11807
12490
|
lines.push(` assert result.${af.field} ${op_} ${af.value}`);
|
|
@@ -12023,11 +12706,19 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
|
|
|
12023
12706
|
const modelName = plan.responseModelName;
|
|
12024
12707
|
const fixtureName = `${fileName$2(modelName)}.json`;
|
|
12025
12708
|
const modelClass = className$5(modelName);
|
|
12709
|
+
const asyncResolvedClass = resolvePaginatedItemClass(modelName, spec) ?? modelClass;
|
|
12710
|
+
const asyncResolvedModelName = asyncResolvedClass !== modelClass ? (() => {
|
|
12711
|
+
const disc = spec.models.find((m) => m.name === modelName)?.discriminator;
|
|
12712
|
+
if (disc) {
|
|
12713
|
+
for (const variantName of Object.values(disc.mapping)) if (className$5(variantName) === asyncResolvedClass) return variantName;
|
|
12714
|
+
}
|
|
12715
|
+
return modelName;
|
|
12716
|
+
})() : modelName;
|
|
12026
12717
|
pushAsyncTestDef(lines, ` async def test_${method}(self, async_workos, httpx_mock):`);
|
|
12027
12718
|
lines.push(` httpx_mock.add_response(json=load_fixture("${fixtureName}"))`);
|
|
12028
12719
|
lines.push(` result = await async_workos.${propName}.${method}(${asyncArgs})`);
|
|
12029
|
-
lines.push(` assert isinstance(result, ${
|
|
12030
|
-
const assertFields = pickAssertableFields(
|
|
12720
|
+
lines.push(` assert isinstance(result, ${asyncResolvedClass})`);
|
|
12721
|
+
const assertFields = pickAssertableFields(asyncResolvedModelName, spec);
|
|
12031
12722
|
for (const af of assertFields) {
|
|
12032
12723
|
const op_ = af.isBool ? "is" : "==";
|
|
12033
12724
|
lines.push(` assert result.${af.field} ${op_} ${af.value}`);
|
|
@@ -12154,7 +12845,7 @@ function emitWrapperTests$1(lines, resolvedOps, propName, spec, ctx, isAsync) {
|
|
|
12154
12845
|
for (const wrapper of r.wrappers) {
|
|
12155
12846
|
const method = wrapper.name;
|
|
12156
12847
|
const wrapperParams = resolveWrapperParams(wrapper, ctx);
|
|
12157
|
-
const
|
|
12848
|
+
const resolvedResponseClass = wrapper.responseModelName ? resolvePaginatedItemClass(wrapper.responseModelName, spec) ?? className$5(wrapper.responseModelName) : null;
|
|
12158
12849
|
const fixtureName = wrapper.responseModelName ? `${fileName$2(wrapper.responseModelName)}.json` : null;
|
|
12159
12850
|
const argParts = [];
|
|
12160
12851
|
for (const { paramName, field, isOptional } of wrapperParams) {
|
|
@@ -12170,7 +12861,7 @@ function emitWrapperTests$1(lines, resolvedOps, propName, spec, ctx, isAsync) {
|
|
|
12170
12861
|
if (fixtureName) {
|
|
12171
12862
|
lines.push(` httpx_mock.add_response(json=load_fixture("${fixtureName}"))`);
|
|
12172
12863
|
lines.push(` result = await async_workos.${propName}.${method}(${args})`);
|
|
12173
|
-
if (
|
|
12864
|
+
if (resolvedResponseClass) lines.push(` assert isinstance(result, ${resolvedResponseClass})`);
|
|
12174
12865
|
} else {
|
|
12175
12866
|
lines.push(" httpx_mock.add_response(json={})");
|
|
12176
12867
|
lines.push(` await async_workos.${propName}.${method}(${args})`);
|
|
@@ -12180,7 +12871,7 @@ function emitWrapperTests$1(lines, resolvedOps, propName, spec, ctx, isAsync) {
|
|
|
12180
12871
|
if (fixtureName) {
|
|
12181
12872
|
lines.push(` httpx_mock.add_response(json=load_fixture("${fixtureName}"))`);
|
|
12182
12873
|
lines.push(` result = workos.${propName}.${method}(${args})`);
|
|
12183
|
-
if (
|
|
12874
|
+
if (resolvedResponseClass) lines.push(` assert isinstance(result, ${resolvedResponseClass})`);
|
|
12184
12875
|
} else {
|
|
12185
12876
|
lines.push(" httpx_mock.add_response(json={})");
|
|
12186
12877
|
lines.push(` workos.${propName}.${method}(${args})`);
|
|
@@ -12950,8 +13641,9 @@ function generateModels$5(models, ctx) {
|
|
|
12950
13641
|
overwriteExisting: true
|
|
12951
13642
|
});
|
|
12952
13643
|
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
|
|
13644
|
+
const listMetadataNeeded = collectReferencedListMetadataModels(models, nonPaginatedRefs);
|
|
12953
13645
|
for (const model of models) {
|
|
12954
|
-
if (isListMetadataModel(model)) continue;
|
|
13646
|
+
if (isListMetadataModel(model) && !listMetadataNeeded.has(model.name)) continue;
|
|
12955
13647
|
if (isListWrapperModel(model) && !nonPaginatedRefs.has(model.name)) continue;
|
|
12956
13648
|
const name = className$4(model.name);
|
|
12957
13649
|
const lines = [];
|
|
@@ -13484,7 +14176,8 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
|
|
|
13484
14176
|
const method = resolveMethodName$4(op, service, ctx);
|
|
13485
14177
|
const hiddenParams = new Set([...Object.keys(getOpDefaults(resolvedOp)), ...getOpInferFromClient(resolvedOp)]);
|
|
13486
14178
|
const isRedirect = isRedirectEndpoint(op, resolvedOp);
|
|
13487
|
-
const
|
|
14179
|
+
const materializeQueryDefaults = !isRedirect;
|
|
14180
|
+
const params = buildMethodParams(op, plan, modelMap, ctx, hiddenParams, { materializeQueryDefaults });
|
|
13488
14181
|
const returnType = isRedirect ? "string" : getReturnType(plan, ctx);
|
|
13489
14182
|
const docParts = [];
|
|
13490
14183
|
if (op.description) docParts.push(op.description);
|
|
@@ -13532,7 +14225,7 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
|
|
|
13532
14225
|
const phpName = fieldName$4(q.name);
|
|
13533
14226
|
if (seenDocParams.has(phpName)) continue;
|
|
13534
14227
|
seenDocParams.add(phpName);
|
|
13535
|
-
const hasEnumDefault = q
|
|
14228
|
+
const hasEnumDefault = shouldMaterializeQueryDefault(q, materializeQueryDefaults);
|
|
13536
14229
|
const nullSuffix = !q.required && !hasEnumDefault && !docType.endsWith("|null") ? "|null" : "";
|
|
13537
14230
|
const prefix = q.deprecated ? "(deprecated) " : "";
|
|
13538
14231
|
let desc = q.description ? ` ${prefix}${q.description}` : q.deprecated ? " (deprecated)" : "";
|
|
@@ -13563,7 +14256,7 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
|
|
|
13563
14256
|
const httpMethod = op.httpMethod.toUpperCase();
|
|
13564
14257
|
const path = buildPathString(op);
|
|
13565
14258
|
if (isRedirect) {
|
|
13566
|
-
const queryLines = buildQueryArray(op, hiddenParams);
|
|
14259
|
+
const queryLines = buildQueryArray(op, hiddenParams, { materializeQueryDefaults });
|
|
13567
14260
|
const hasDefaults = Object.keys(getOpDefaults(resolvedOp)).length > 0;
|
|
13568
14261
|
const hasInferred = getOpInferFromClient(resolvedOp).length > 0;
|
|
13569
14262
|
const hasGroups = (op.parameterGroups?.length ?? 0) > 0;
|
|
@@ -13707,7 +14400,7 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
|
|
|
13707
14400
|
}
|
|
13708
14401
|
lines.push(" }");
|
|
13709
14402
|
}
|
|
13710
|
-
function buildMethodParams(op, plan, modelMap, ctx, hiddenParams) {
|
|
14403
|
+
function buildMethodParams(op, plan, modelMap, ctx, hiddenParams, opts) {
|
|
13711
14404
|
const required = [];
|
|
13712
14405
|
const optional = [];
|
|
13713
14406
|
const usedNames = /* @__PURE__ */ new Set();
|
|
@@ -13755,7 +14448,7 @@ function buildMethodParams(op, plan, modelMap, ctx, hiddenParams) {
|
|
|
13755
14448
|
if (usedNames.has(phpName)) continue;
|
|
13756
14449
|
usedNames.add(phpName);
|
|
13757
14450
|
if (q.required) required.push(`${phpType} $${phpName}`);
|
|
13758
|
-
else if (q
|
|
14451
|
+
else if (shouldMaterializeQueryDefault(q, opts?.materializeQueryDefaults ?? true)) {
|
|
13759
14452
|
const enumType = mapTypeRef$5(q.type, { qualified: true });
|
|
13760
14453
|
const caseName = toPascalCase(String(q.default));
|
|
13761
14454
|
optional.push(`${enumType} $${phpName} = ${enumType}::${caseName}`);
|
|
@@ -13801,19 +14494,31 @@ function isEnumType(ref) {
|
|
|
13801
14494
|
if (ref.kind === "nullable") return isEnumType(ref.inner);
|
|
13802
14495
|
return false;
|
|
13803
14496
|
}
|
|
13804
|
-
function
|
|
14497
|
+
function isDateTimeType(ref) {
|
|
14498
|
+
if (ref.kind === "primitive" && ref.format === "date-time") return true;
|
|
14499
|
+
if (ref.kind === "nullable") return isDateTimeType(ref.inner);
|
|
14500
|
+
return false;
|
|
14501
|
+
}
|
|
14502
|
+
function buildQueryArray(op, hiddenParams, opts) {
|
|
13805
14503
|
const hidden = hiddenParams ?? /* @__PURE__ */ new Set();
|
|
13806
14504
|
const groupedParams = collectGroupedParamNames(op);
|
|
13807
14505
|
return op.queryParams.filter((q) => !hidden.has(q.name) && !groupedParams.has(q.name)).map((q) => {
|
|
13808
14506
|
const phpName = fieldName$4(q.name);
|
|
13809
14507
|
if (isEnumType(q.type)) {
|
|
13810
|
-
const hasEnumDefault = q
|
|
14508
|
+
const hasEnumDefault = shouldMaterializeQueryDefault(q, opts?.materializeQueryDefaults ?? true);
|
|
13811
14509
|
const nullsafe = q.required || hasEnumDefault ? "" : "?";
|
|
13812
14510
|
return `'${q.name}' => $${phpName}${nullsafe}->value,`;
|
|
13813
14511
|
}
|
|
14512
|
+
if (isDateTimeType(q.type)) {
|
|
14513
|
+
const nullsafe = q.required ? "" : "?";
|
|
14514
|
+
return `'${q.name}' => $${phpName}${nullsafe}->format(\\DateTimeInterface::RFC3339_EXTENDED),`;
|
|
14515
|
+
}
|
|
13814
14516
|
return `'${q.name}' => $${phpName},`;
|
|
13815
14517
|
});
|
|
13816
14518
|
}
|
|
14519
|
+
function shouldMaterializeQueryDefault(param, enabled) {
|
|
14520
|
+
return enabled && param.default != null && param.type.kind === "enum";
|
|
14521
|
+
}
|
|
13817
14522
|
function phpLiteral(value) {
|
|
13818
14523
|
if (typeof value === "string") return `'${value}'`;
|
|
13819
14524
|
if (typeof value === "number") return String(value);
|
|
@@ -14005,8 +14710,10 @@ function generateFixtures$3(spec) {
|
|
|
14005
14710
|
const modelMap = new Map(spec.models.map((m) => [m.name, m]));
|
|
14006
14711
|
const enumMap = new Map(spec.enums.map((e) => [e.name, e]));
|
|
14007
14712
|
const files = [];
|
|
14713
|
+
const nonPaginatedRefs = collectNonPaginatedResponseModelNames(spec.services);
|
|
14714
|
+
const listMetadataNeeded = collectReferencedListMetadataModels(spec.models, nonPaginatedRefs);
|
|
14008
14715
|
for (const model of spec.models) {
|
|
14009
|
-
if (isListMetadataModel(model)) continue;
|
|
14716
|
+
if (isListMetadataModel(model) && !listMetadataNeeded.has(model.name)) continue;
|
|
14010
14717
|
if (isListWrapperModel(model)) continue;
|
|
14011
14718
|
const fixture = generateModelFixture$3(model, modelMap, enumMap);
|
|
14012
14719
|
files.push({
|
|
@@ -14351,13 +15058,15 @@ function buildTestArgs(op, ctx, opts) {
|
|
|
14351
15058
|
}
|
|
14352
15059
|
function generateTestValue(ref, ctx) {
|
|
14353
15060
|
switch (ref.kind) {
|
|
14354
|
-
case "primitive":
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
|
|
14359
|
-
|
|
14360
|
-
|
|
15061
|
+
case "primitive":
|
|
15062
|
+
if (ref.format === "date-time") return "new \\DateTimeImmutable('2023-01-01T00:00:00Z')";
|
|
15063
|
+
switch (ref.type) {
|
|
15064
|
+
case "string": return "'test_value'";
|
|
15065
|
+
case "integer": return "1";
|
|
15066
|
+
case "number": return "1.0";
|
|
15067
|
+
case "boolean": return "true";
|
|
15068
|
+
default: return "'test_value'";
|
|
15069
|
+
}
|
|
14361
15070
|
case "enum":
|
|
14362
15071
|
if (ctx && ref.name) {
|
|
14363
15072
|
const e = ctx.spec.enums.find((en) => en.name === ref.name);
|
|
@@ -14444,7 +15153,8 @@ function emitQueryAssertions(lines, op, ctx, hidden) {
|
|
|
14444
15153
|
if (innerType.kind === "enum" && innerType.name) {
|
|
14445
15154
|
const e = ctx.spec.enums.find((en) => en.name === innerType.name);
|
|
14446
15155
|
if (e && e.values.length > 0) lines.push(` $this->assertSame('${e.values[0].value}', $query['${q.name}']);`);
|
|
14447
|
-
} else if (innerType.kind === "primitive")
|
|
15156
|
+
} else if (innerType.kind === "primitive") if (innerType.format === "date-time") lines.push(` $this->assertArrayHasKey('${q.name}', $query);`);
|
|
15157
|
+
else switch (innerType.type) {
|
|
14448
15158
|
case "string":
|
|
14449
15159
|
lines.push(` $this->assertSame('test_value', $query['${q.name}']);`);
|
|
14450
15160
|
break;
|
|
@@ -15154,6 +15864,8 @@ function generateEnums$4(enums, ctx) {
|
|
|
15154
15864
|
content: lines.join("\n"),
|
|
15155
15865
|
overwriteExisting: true
|
|
15156
15866
|
});
|
|
15867
|
+
const eventConstantsFile = generateEventConstantsFile(enums);
|
|
15868
|
+
if (eventConstantsFile) files.push(eventConstantsFile);
|
|
15157
15869
|
return files;
|
|
15158
15870
|
}
|
|
15159
15871
|
/** Known acronyms to preserve as single tokens during humanization. */
|
|
@@ -15214,6 +15926,56 @@ function collectEnumAliasOf$2(enums) {
|
|
|
15214
15926
|
}
|
|
15215
15927
|
return aliasOf;
|
|
15216
15928
|
}
|
|
15929
|
+
function generateEventConstantsFile(enums) {
|
|
15930
|
+
const enumDef = findWebhookEventEnum(enums);
|
|
15931
|
+
if (!enumDef) return null;
|
|
15932
|
+
const lines = [];
|
|
15933
|
+
lines.push("package events");
|
|
15934
|
+
lines.push("");
|
|
15935
|
+
lines.push("// Event is a WorkOS event type.");
|
|
15936
|
+
lines.push("type Event string");
|
|
15937
|
+
lines.push("");
|
|
15938
|
+
lines.push("const (");
|
|
15939
|
+
const seenValues = /* @__PURE__ */ new Set();
|
|
15940
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
15941
|
+
for (const value of enumDef.values) {
|
|
15942
|
+
const valueStr = String(value.value);
|
|
15943
|
+
if (seenValues.has(valueStr)) continue;
|
|
15944
|
+
seenValues.add(valueStr);
|
|
15945
|
+
const constName = uniqueEventConstantName(valueStr, usedNames);
|
|
15946
|
+
usedNames.add(constName);
|
|
15947
|
+
if (value.description) lines.push(`\t// ${constName} is ${value.description}.`);
|
|
15948
|
+
if (value.deprecated) {
|
|
15949
|
+
if (value.description) lines.push(" //");
|
|
15950
|
+
lines.push(" // Deprecated: this value is deprecated.");
|
|
15951
|
+
}
|
|
15952
|
+
lines.push(`\t${constName} = "${escapeGoString$1(valueStr)}"`);
|
|
15953
|
+
}
|
|
15954
|
+
lines.push(")");
|
|
15955
|
+
lines.push("");
|
|
15956
|
+
return {
|
|
15957
|
+
path: "pkg/events/events.go",
|
|
15958
|
+
content: lines.join("\n"),
|
|
15959
|
+
overwriteExisting: true
|
|
15960
|
+
};
|
|
15961
|
+
}
|
|
15962
|
+
function findWebhookEventEnum(enums) {
|
|
15963
|
+
return enums.find((enumDef) => enumDef.name === "CreateWebhookEndpointEvents") ?? enums.find((enumDef) => isWebhookEventEnumName(enumDef.name) && enumDef.values.length > 0 && enumDef.values.every((value) => typeof value.value === "string" && value.value.includes("."))) ?? null;
|
|
15964
|
+
}
|
|
15965
|
+
function isWebhookEventEnumName(name) {
|
|
15966
|
+
const normalized = name.toLowerCase();
|
|
15967
|
+
return normalized.includes("webhook") && normalized.includes("event");
|
|
15968
|
+
}
|
|
15969
|
+
function uniqueEventConstantName(value, usedNames) {
|
|
15970
|
+
const base = className$3(value);
|
|
15971
|
+
if (!usedNames.has(base)) return base;
|
|
15972
|
+
let suffix = 2;
|
|
15973
|
+
while (usedNames.has(`${base}${suffix}`)) suffix++;
|
|
15974
|
+
return `${base}${suffix}`;
|
|
15975
|
+
}
|
|
15976
|
+
function escapeGoString$1(value) {
|
|
15977
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
15978
|
+
}
|
|
15217
15979
|
//#endregion
|
|
15218
15980
|
//#region src/go/wrappers.ts
|
|
15219
15981
|
/**
|
|
@@ -16262,7 +17024,7 @@ function generateClient$4(spec, ctx) {
|
|
|
16262
17024
|
return [generateWorkOSFile(spec, ctx)];
|
|
16263
17025
|
}
|
|
16264
17026
|
/**
|
|
16265
|
-
* Non-spec services marked with `hasClientAccessor: true` (passwordless
|
|
17027
|
+
* Non-spec services marked with `hasClientAccessor: true` (e.g. passwordless)
|
|
16266
17028
|
* are included in the generated Client struct, constructor, and accessor methods
|
|
16267
17029
|
* — identical to spec-driven services. Their service type (e.g. PasswordlessService)
|
|
16268
17030
|
* is defined in a hand-written @oagen-ignore-file, but the Client wiring is generated.
|
|
@@ -20031,12 +20793,19 @@ function generateEnums$2(enums, _ctx) {
|
|
|
20031
20793
|
hashGroups.get(hash).push(enumDef);
|
|
20032
20794
|
}
|
|
20033
20795
|
const aliasOf = /* @__PURE__ */ new Map();
|
|
20796
|
+
const sharedSortEmitters = /* @__PURE__ */ new Set();
|
|
20034
20797
|
for (const [, group] of hashGroups) {
|
|
20035
|
-
if (group.length <= 1)
|
|
20798
|
+
if (group.length <= 1) {
|
|
20799
|
+
if (group.length === 1 && isSharedSortOrderEnum(group[0])) {
|
|
20800
|
+
enumCanonicalMap.set(group[0].name, "SortOrder");
|
|
20801
|
+
sharedSortEmitters.add(group[0].name);
|
|
20802
|
+
}
|
|
20803
|
+
continue;
|
|
20804
|
+
}
|
|
20036
20805
|
if (group.every(isSharedSortOrderEnum)) {
|
|
20037
20806
|
const [canonical, ...rest] = [...group].sort((a, b) => a.name.localeCompare(b.name));
|
|
20038
|
-
|
|
20039
|
-
for (const enumDef of rest) enumCanonicalMap.set(enumDef.name, "SortOrder");
|
|
20807
|
+
sharedSortEmitters.add(canonical.name);
|
|
20808
|
+
for (const enumDef of [canonical, ...rest]) enumCanonicalMap.set(enumDef.name, "SortOrder");
|
|
20040
20809
|
continue;
|
|
20041
20810
|
}
|
|
20042
20811
|
const sorted = [...group].sort((a, b) => className$1(a.name).length - className$1(b.name).length || className$1(a.name).localeCompare(className$1(b.name)));
|
|
@@ -20050,7 +20819,7 @@ function generateEnums$2(enums, _ctx) {
|
|
|
20050
20819
|
for (const enumDef of enums) {
|
|
20051
20820
|
if (enumDef.values.length === 0) continue;
|
|
20052
20821
|
const typeName = canonicalEnumTypeName(enumDef);
|
|
20053
|
-
const canonicalName =
|
|
20822
|
+
const canonicalName = sharedSortEmitters.has(enumDef.name) ? void 0 : aliasOf.get(enumDef.name) ?? enumCanonicalMap.get(enumDef.name);
|
|
20054
20823
|
if (canonicalName) {
|
|
20055
20824
|
const canonicalType = className$1(canonicalName);
|
|
20056
20825
|
if (typeName === canonicalType) continue;
|
|
@@ -24298,11 +25067,6 @@ const NON_SPEC_ACCESSORS = {
|
|
|
24298
25067
|
className: "Passwordless",
|
|
24299
25068
|
ctorArg: "self"
|
|
24300
25069
|
},
|
|
24301
|
-
vault: {
|
|
24302
|
-
prop: "vault",
|
|
24303
|
-
className: "Vault",
|
|
24304
|
-
ctorArg: "self"
|
|
24305
|
-
},
|
|
24306
25070
|
actions: {
|
|
24307
25071
|
prop: "actions",
|
|
24308
25072
|
className: "Actions",
|
|
@@ -26119,6 +26883,7 @@ function classifyGroup(group, op) {
|
|
|
26119
26883
|
function renderParamsStruct(name, op, resolved, registry, ctx, groupEmitter) {
|
|
26120
26884
|
const bodyRequired = isBodyRequired(op);
|
|
26121
26885
|
const hidden = new Set([...Object.keys(resolved.defaults ?? {}), ...resolved.inferFromClient ?? []]);
|
|
26886
|
+
const materializeSpecDefaults = !resolved.urlBuilder;
|
|
26122
26887
|
const queryGroupParamNames = /* @__PURE__ */ new Set();
|
|
26123
26888
|
const bodyGroupParamNames = /* @__PURE__ */ new Set();
|
|
26124
26889
|
const queryGroupFields = [];
|
|
@@ -26155,7 +26920,7 @@ function renderParamsStruct(name, op, resolved, registry, ctx, groupEmitter) {
|
|
|
26155
26920
|
});
|
|
26156
26921
|
if (!p.required && !rust.startsWith("Option<")) rust = makeOptional(rust);
|
|
26157
26922
|
rust = applySecretRedaction(rust, p.name);
|
|
26158
|
-
const defaultExpr = p.default != null ? rustDefaultExpr(p.default, p.type, rust.startsWith("Option<"), ctx) : null;
|
|
26923
|
+
const defaultExpr = materializeSpecDefaults && p.default != null ? rustDefaultExpr(p.default, p.type, rust.startsWith("Option<"), ctx) : null;
|
|
26159
26924
|
const desc = p.description?.trim();
|
|
26160
26925
|
if (desc) for (const c of paramDocComment(desc)) fieldLines.push(` ${c}`);
|
|
26161
26926
|
if (p.default != null) {
|
|
@@ -26788,7 +27553,7 @@ function renderResourcesBarrel(exports) {
|
|
|
26788
27553
|
}
|
|
26789
27554
|
unique.sort((a, b) => a.module.localeCompare(b.module));
|
|
26790
27555
|
const lines = [];
|
|
26791
|
-
for (const { module } of unique) lines.push(`mod ${module};`);
|
|
27556
|
+
for (const { module } of unique) lines.push(`pub mod ${module};`);
|
|
26792
27557
|
lines.push("");
|
|
26793
27558
|
for (const { module, struct } of unique) lines.push(`pub use ${module}::${struct};`);
|
|
26794
27559
|
return lines.join("\n") + "\n";
|
|
@@ -27650,4 +28415,4 @@ const workosEmittersPlugin = {
|
|
|
27650
28415
|
//#endregion
|
|
27651
28416
|
export { pythonEmitter as _, rustExtractor as a, pythonExtractor as c, rustEmitter as d, rubyEmitter as f, phpEmitter as g, goEmitter as h, kotlinExtractor as i, rubyExtractor as l, dotnetEmitter as m, elixirExtractor as n, goExtractor as o, kotlinEmitter as p, dotnetExtractor as r, phpExtractor as s, workosEmittersPlugin as t, nodeExtractor as u, nodeEmitter as v };
|
|
27652
28417
|
|
|
27653
|
-
//# sourceMappingURL=plugin-
|
|
28418
|
+
//# sourceMappingURL=plugin-C2Hp2Vs2.mjs.map
|