@workos/oagen-emitters 0.14.4 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/index.d.mts.map +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/dist/{plugin-BGVaMGqe.mjs → plugin-CO4RFgAW.mjs} +959 -251
  6. package/dist/plugin-CO4RFgAW.mjs.map +1 -0
  7. package/dist/plugin.mjs +1 -1
  8. package/package.json +7 -7
  9. package/renovate.json +1 -61
  10. package/src/go/client.ts +1 -1
  11. package/src/go/enums.ts +77 -0
  12. package/src/kotlin/enums.ts +11 -4
  13. package/src/node/client.ts +119 -2
  14. package/src/node/discriminated-models.ts +8 -0
  15. package/src/node/field-plan.ts +64 -8
  16. package/src/node/index.ts +59 -3
  17. package/src/node/models.ts +73 -30
  18. package/src/node/naming.ts +14 -1
  19. package/src/node/node-overrides.ts +4 -37
  20. package/src/node/options.ts +29 -1
  21. package/src/node/resources.ts +533 -83
  22. package/src/node/tests.ts +108 -7
  23. package/src/php/fixtures.ts +4 -1
  24. package/src/php/models.ts +3 -1
  25. package/src/php/resources.ts +40 -11
  26. package/src/php/tests.ts +22 -12
  27. package/src/python/client.ts +0 -8
  28. package/src/python/enums.ts +41 -15
  29. package/src/python/fixtures.ts +23 -7
  30. package/src/python/models.ts +26 -5
  31. package/src/python/resources.ts +71 -3
  32. package/src/python/tests.ts +70 -12
  33. package/src/python/wrappers.ts +25 -4
  34. package/src/ruby/client.ts +0 -1
  35. package/src/rust/resources.ts +10 -7
  36. package/src/shared/non-spec-services.ts +0 -5
  37. package/test/go/enums.test.ts +24 -0
  38. package/test/node/resources.test.ts +11 -1
  39. package/test/node/tests.test.ts +3 -3
  40. package/test/php/client.test.ts +0 -1
  41. package/test/php/resources.test.ts +50 -0
  42. package/test/rust/resources.test.ts +9 -0
  43. 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 baselineSrc = (sctx.ctx.apiSurface?.interfaces?.[depName])?.sourceFile;
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 (!shouldSkipSerialize) {
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
- //#endregion
4308
- //#region src/node/options.ts
4309
- function nodeOptions(ctx) {
4310
- return ctx.emitterOptions ?? {};
4311
- }
4312
- function normalizeServiceName(name) {
4313
- return name.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
4314
- }
4315
- function isNodeOwnedService(ctx, ...names) {
4316
- const configured = nodeOptions(ctx).ownedServices ?? [];
4317
- if (configured.length === 0) return false;
4318
- const owned = new Set(configured.map(normalizeServiceName));
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,10 +4730,56 @@ 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: param.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
4784
  return returnType?.match(/\bAutoPaginatable<\s*([A-Za-z_$][\w$]*)/)?.[1];
4654
4785
  }
@@ -4738,43 +4869,83 @@ function deduplicateMethodNames(plans, _ctx) {
4738
4869
  }
4739
4870
  }
4740
4871
  /**
4741
- * Emit one interface file per paginated list operation that has extension
4742
- * query params. Placing the options interface under `interfaces/` lets the
4743
- * per-service barrel pick it up via `export * from './interfaces'`, which
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.
4872
+ * Emit one interface/type file per generated options-object operation.
4873
+ * Placing the options type under `interfaces/` lets the per-service barrel
4874
+ * pick it up via `export * from './interfaces'`.
4747
4875
  */
4748
- function generatePaginatedOptionsInterfaces(service, ctx, specEnumNames) {
4876
+ function generateOptionsInterfaces(service, ctx, specEnumNames) {
4749
4877
  const files = [];
4750
- const resolvedName = resolveResourceClassName$3(service, ctx);
4751
4878
  const serviceDir = resolveResourceDir(service, ctx);
4879
+ const resolvedLookup = buildResolvedLookup(ctx);
4752
4880
  const plans = service.operations.map((op) => ({
4753
4881
  op,
4754
4882
  plan: planOperation(op),
4755
4883
  method: resolveMethodName$6(op, service, ctx)
4756
4884
  }));
4757
4885
  for (const { op, plan, method } of plans) {
4758
- if (!plan.isPaginated) continue;
4759
- const extraParams = op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name));
4760
- if (extraParams.length === 0) continue;
4761
- const optionsName = paginatedOptionsName(method, resolvedName);
4762
- const filePath = `src/${serviceDir}/interfaces/${fileName$3(optionsName)}.interface.ts`;
4886
+ const resolvedOp = lookupResolved(op, resolvedLookup);
4887
+ const optionInfo = optionsObjectInfo(service, method, op, plan, ctx, baselineMethodFor$1(service, method, ctx), resolvedOp);
4888
+ if (!optionInfo?.generated) continue;
4889
+ if (baselineTypeSourceFile(ctx, optionInfo.type)) continue;
4890
+ const optionsName = optionInfo.type;
4891
+ const optionFileStem = `${fileName$3(optionsName)}.interface`;
4892
+ const filePath = `src/${serviceDir}/interfaces/${optionFileStem}.ts`;
4893
+ if (!liveSurfaceHasFile(filePath) || existingInterfaceBarrelExports(ctx, serviceDir, optionFileStem)) recordGeneratedOptionInterface(ctx, serviceDir, optionFileStem, optionsName);
4894
+ const optEnums = /* @__PURE__ */ new Set();
4895
+ const optModels = /* @__PURE__ */ new Set();
4896
+ for (const param of [...op.pathParams, ...visibleQueryParamsForOptions(op, plan, resolvedOp)]) collectParamTypeRefs(param.type, optEnums, optModels);
4897
+ const bodyInfo = extractRequestBodyType(op, ctx);
4898
+ if (bodyInfo?.kind === "model") {
4899
+ const bodyModel = ctx.spec.models.find((m) => m.name === bodyInfo.name);
4900
+ if (bodyModel) for (const field of bodyModel.fields) collectParamTypeRefs(field.type, optEnums, optModels);
4901
+ } else if (bodyInfo?.kind === "union") for (const name of bodyInfo.modelNames) optModels.add(name);
4763
4902
  const lines = [];
4764
- lines.push("import type { PaginationOptions } from '../../common/interfaces/pagination-options.interface';");
4903
+ if (plan.isPaginated) lines.push("import type { PaginationOptions } from '../../common/interfaces/pagination-options.interface';");
4904
+ const { modelToService, resolveDir } = createServiceDirResolver(ctx.spec.models, ctx.spec.services, ctx);
4905
+ if (optEnums.size > 0) {
4906
+ const enumToService = assignEnumsToServices(ctx.spec.enums, ctx.spec.services, ctx.spec.models, ctx);
4907
+ for (const name of optEnums) {
4908
+ if (!specEnumNames.has(name)) continue;
4909
+ if (isInlineEnum(name)) continue;
4910
+ const enumDir = resolveDir(enumToService.get(name));
4911
+ const relPath = enumDir === serviceDir ? `./${fileName$3(name)}.interface` : `../../${enumDir}/interfaces/${fileName$3(name)}.interface`;
4912
+ lines.push(`import type { ${name} } from '${relPath}';`);
4913
+ }
4914
+ }
4915
+ for (const name of optModels) {
4916
+ const modelDir = resolveDir(modelToService.get(name));
4917
+ const relPath = modelDir === serviceDir ? `./${fileName$3(name)}.interface` : `../../${modelDir}/interfaces/${fileName$3(name)}.interface`;
4918
+ lines.push(`import type { ${resolveInterfaceName(name, ctx)} } from '${relPath}';`);
4919
+ }
4765
4920
  lines.push("");
4766
- lines.push(`export interface ${optionsName} extends PaginationOptions {`);
4767
- for (const param of extraParams) {
4768
- const opt = !param.required ? "?" : "";
4769
- if (param.description || param.deprecated) {
4921
+ const headerParts = [];
4922
+ const pushField = (name, required, type, description, deprecated) => {
4923
+ const opt = !required ? "?" : "";
4924
+ if (description || deprecated) {
4770
4925
  const parts = [];
4771
- if (param.description) parts.push(param.description);
4772
- if (param.deprecated) parts.push("@deprecated");
4773
- lines.push(...docComment$2(parts.join("\n"), 2));
4926
+ if (description) parts.push(description);
4927
+ if (deprecated) parts.push("@deprecated");
4928
+ headerParts.push(...docComment$2(parts.join("\n"), 2));
4774
4929
  }
4775
- lines.push(` ${fieldName$6(param.name)}${opt}: ${mapParamType(param.type, specEnumNames)};`);
4930
+ headerParts.push(` ${name}${opt}: ${type};`);
4931
+ };
4932
+ for (const param of op.pathParams) pushField(fieldName$6(param.name), true, mapParamType(param.type, specEnumNames), param.description, param.deprecated);
4933
+ for (const param of visibleQueryParamsForOptions(op, plan, resolvedOp)) pushField(fieldName$6(param.name), param.required, mapParamType(param.type, specEnumNames), param.description, param.deprecated);
4934
+ if (bodyInfo?.kind === "model") {
4935
+ const bodyModel = ctx.spec.models.find((m) => m.name === bodyInfo.name);
4936
+ if (bodyModel) for (const field of bodyModel.fields) pushField(fieldName$6(field.name), field.required, mapParamType(field.type, specEnumNames), field.description, field.deprecated);
4937
+ lines.push(`export interface ${optionsName}${plan.isPaginated ? " extends PaginationOptions" : ""} {`);
4938
+ lines.push(...headerParts);
4939
+ lines.push("}");
4940
+ } else if (bodyInfo?.kind === "union") {
4941
+ const baseType = headerParts.length > 0 ? `{\n${headerParts.join("\n")}\n}` : "{}";
4942
+ lines.push(`export type ${optionsName} = ${baseType} & (${bodyInfo.typeStr});`);
4943
+ } else if (plan.isPaginated && headerParts.length === 0) lines.push(`export type ${optionsName} = PaginationOptions;`);
4944
+ else {
4945
+ lines.push(`export interface ${optionsName}${plan.isPaginated ? " extends PaginationOptions" : ""} {`);
4946
+ lines.push(...headerParts);
4947
+ lines.push("}");
4776
4948
  }
4777
- lines.push("}");
4778
4949
  files.push({
4779
4950
  path: filePath,
4780
4951
  content: lines.join("\n")
@@ -4819,7 +4990,7 @@ function generateResources$7(services, ctx) {
4819
4990
  }
4820
4991
  for (const service of mergedServices) {
4821
4992
  if (!isNodeOwnedService(ctx, service.name, resolveResourceClassName$3(service, ctx)) && isServiceCoveredByExisting(service, ctx) && !hasMethodsAbsentFromBaseline(service, ctx)) continue;
4822
- files.push(...generatePaginatedOptionsInterfaces(service, ctx, topLevelEnumNames));
4993
+ files.push(...generateOptionsInterfaces(service, ctx, topLevelEnumNames));
4823
4994
  }
4824
4995
  return files;
4825
4996
  }
@@ -4844,27 +5015,40 @@ function generateResourceClass(service, ctx) {
4844
5015
  return (httpKeyOrder.get(aKey) ?? Number.MAX_SAFE_INTEGER) - (httpKeyOrder.get(bKey) ?? Number.MAX_SAFE_INTEGER);
4845
5016
  });
4846
5017
  }
5018
+ const ignoredMethodNames = ignoredResourceMethodNames(ctx, resourcePath);
4847
5019
  const baselineMethodNames = new Set(Object.keys(ctx.apiSurface?.classes?.[serviceClass]?.methods ?? {}));
4848
5020
  const planCountBeforeFilter = plans.length;
5021
+ if (ignoredMethodNames.size > 0) plans = plans.filter((p) => !ignoredMethodNames.has(p.method));
4849
5022
  if (!isNodeOwnedService(ctx, service.name, serviceClass) && baselineMethodNames.size > 0) plans = plans.filter((p) => !baselineMethodNames.has(p.method));
4850
5023
  const filteredOut = planCountBeforeFilter - plans.length;
4851
5024
  if (plans.length === 0 && filteredOut > 0) return null;
4852
5025
  const hasPaginated = plans.some((p) => p.plan.isPaginated);
4853
- const needsPaginationOptionsImport = plans.some((p) => p.plan.isPaginated && (!optionsObjectParam$1(baselineMethodFor$1(service, p.method, ctx)) || /\bPaginationOptions\b/.test(preferredBaselineReturnType(ctx, baselineMethodFor$1(service, p.method, ctx)?.returnType) ?? "")));
5026
+ const resolvedLookup = buildResolvedLookup(ctx);
5027
+ const needsPaginationOptionsImport = plans.some((p) => {
5028
+ const baseline = baselineMethodFor$1(service, p.method, ctx);
5029
+ const optionInfo = optionsObjectInfo(service, p.method, p.op, p.plan, ctx, baseline, lookupResolved(p.op, resolvedLookup));
5030
+ const needsWireSerializer = p.op.queryParams.filter((param) => !PAGINATION_PARAM_NAMES.has(param.name)).some((param) => fieldName$6(param.name) !== wireFieldName(param.name));
5031
+ return p.plan.isPaginated && (needsWireSerializer || !optionInfo || /\bPaginationOptions\b/.test(preferredBaselineReturnType(ctx, baselineMethodFor$1(service, p.method, ctx)?.returnType) ?? ""));
5032
+ });
4854
5033
  const modelMap = new Map(ctx.spec.models.map((m) => [m.name, m]));
4855
5034
  const responseModels = /* @__PURE__ */ new Set();
5035
+ const responseModelsForSignature = /* @__PURE__ */ new Set();
4856
5036
  const requestModels = /* @__PURE__ */ new Set();
4857
5037
  const requestModelsForSignature = /* @__PURE__ */ new Set();
4858
5038
  const paramEnums = /* @__PURE__ */ new Set();
4859
5039
  const paramModels = /* @__PURE__ */ new Set();
4860
5040
  const optionObjectTypes = /* @__PURE__ */ new Set();
5041
+ const returnTypeImports = /* @__PURE__ */ new Set();
4861
5042
  const baselineResponseTypes = /* @__PURE__ */ new Set();
4862
- for (const { op, plan } of plans) {
4863
- const baselineMethod = baselineMethodFor$1(service, resolveMethodName$6(op, service, ctx), ctx);
4864
- const existingOptions = optionsObjectParam$1(baselineMethod);
5043
+ for (const { op, plan, method } of plans) {
5044
+ const baselineMethod = baselineMethodFor$1(service, method, ctx);
5045
+ const existingOptions = optionsObjectInfo(service, method, op, plan, ctx, baselineMethod, lookupResolved(op, resolvedLookup));
4865
5046
  if (existingOptions) optionObjectTypes.add(existingOptions.type);
4866
- const queryParams = plan.isPaginated ? op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name)) : op.queryParams;
4867
- for (const param of [...queryParams, ...op.pathParams]) collectParamTypeRefs(param.type, paramEnums, paramModels);
5047
+ for (const typeName of operationOverrideFor$1(ctx, op)?.returnTypeImports ?? []) returnTypeImports.add(typeName);
5048
+ if (!existingOptions) {
5049
+ const queryParams = plan.isPaginated ? [] : op.queryParams;
5050
+ for (const param of [...queryParams, ...op.pathParams]) collectParamTypeRefs(param.type, paramEnums, paramModels);
5051
+ }
4868
5052
  const baselinePaginatedItemType = existingOptions ? preferredBaselineTypeName(ctx, autoPaginatableItemType$1(baselineMethod?.returnType)) : void 0;
4869
5053
  if (plan.isPaginated && baselinePaginatedItemType) baselineResponseTypes.add(baselinePaginatedItemType);
4870
5054
  else if (plan.isPaginated && op.pagination?.itemType.kind === "model") {
@@ -4875,7 +5059,13 @@ function generateResourceClass(service, ctx) {
4875
5059
  if (unwrapped) itemName = unwrapped.name;
4876
5060
  }
4877
5061
  responseModels.add(itemName);
4878
- } else if (plan.responseModelName) responseModels.add(plan.responseModelName);
5062
+ responseModelsForSignature.add(itemName);
5063
+ } else if (plan.responseModelName) {
5064
+ responseModels.add(plan.responseModelName);
5065
+ const override = operationOverrideFor$1(ctx, op);
5066
+ const resolvedResponseName = resolveInterfaceName(plan.responseModelName, ctx);
5067
+ if (!override?.returnType || override.returnType.includes(resolvedResponseName)) responseModelsForSignature.add(plan.responseModelName);
5068
+ }
4879
5069
  const bodyInfo = extractRequestBodyType(op, ctx);
4880
5070
  if (bodyInfo?.kind === "model") {
4881
5071
  requestModels.add(bodyInfo.name);
@@ -4889,7 +5079,6 @@ function generateResourceClass(service, ctx) {
4889
5079
  if (!existingOptions) requestModelsForSignature.add(name);
4890
5080
  }
4891
5081
  }
4892
- const resolvedLookup = buildResolvedLookup(ctx);
4893
5082
  for (const { op } of plans) {
4894
5083
  const resolved = lookupResolved(op, resolvedLookup);
4895
5084
  if (resolved) {
@@ -4898,7 +5087,7 @@ function generateResourceClass(service, ctx) {
4898
5087
  }
4899
5088
  }
4900
5089
  const allModels = new Set([
4901
- ...responseModels,
5090
+ ...responseModelsForSignature,
4902
5091
  ...requestModelsForSignature,
4903
5092
  ...paramModels
4904
5093
  ]);
@@ -4909,12 +5098,8 @@ function generateResourceClass(service, ctx) {
4909
5098
  lines.push("import { AutoPaginatable } from '../common/utils/pagination';");
4910
5099
  lines.push("import { fetchAndDeserialize } from '../common/utils/fetch-and-deserialize';");
4911
5100
  }
4912
- for (const { op, plan, method } of plans) {
4913
- if (!plan.isPaginated) continue;
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
- }
5101
+ const shouldEmitVaultCryptoHelpers = serviceClass === "Vault" && !ignoredMethodNames.has("encrypt") && !ignoredMethodNames.has("decrypt");
5102
+ if (shouldEmitVaultCryptoHelpers) lines.push("import { base64ToUint8Array, uint8ArrayToBase64 } from '../common/utils/base64';");
4918
5103
  const hasIdempotentPost = plans.some((p) => p.plan.isIdempotentPost);
4919
5104
  const hasCustomEncoding = plans.some((p) => p.op.requestBodyEncoding && p.op.requestBodyEncoding !== "json" && p.plan.hasBody);
4920
5105
  if (hasIdempotentPost || hasCustomEncoding) lines.push("import type { PostOptions } from '../common/interfaces/post-options.interface';");
@@ -4922,7 +5107,16 @@ function generateResourceClass(service, ctx) {
4922
5107
  for (const optionType of optionObjectTypes) {
4923
5108
  if (importedTypeNames.has(optionType)) continue;
4924
5109
  importedTypeNames.add(optionType);
4925
- lines.push(`import type { ${optionType} } from './interfaces/${fileName$3(optionType)}.interface';`);
5110
+ const sourceFile = baselineTypeSourceFile(ctx, optionType);
5111
+ const relPath = sourceFile ? relativeImport(resourcePath, sourceFile) : `./interfaces/${fileName$3(optionType)}.interface`;
5112
+ lines.push(`import type { ${optionType} } from '${relPath}';`);
5113
+ }
5114
+ for (const typeName of returnTypeImports) {
5115
+ if (importedTypeNames.has(typeName)) continue;
5116
+ const sourceFile = baselineTypeSourceFile(ctx, typeName) ?? liveSurfaceInterfacePath(typeName);
5117
+ if (!sourceFile) continue;
5118
+ importedTypeNames.add(typeName);
5119
+ lines.push(`import type { ${typeName} } from '${relativeImport(resourcePath, sourceFile)}';`);
4926
5120
  }
4927
5121
  const { modelToService, resolveDir } = createServiceDirResolver(ctx.spec.models, ctx.spec.services, ctx);
4928
5122
  const usedWireTypes = /* @__PURE__ */ new Set();
@@ -4954,6 +5148,17 @@ function generateResourceClass(service, ctx) {
4954
5148
  lines.push(`import type { ${wireName} } from '${relPath}';`);
4955
5149
  importedTypeNames.add(wireName);
4956
5150
  }
5151
+ for (const name of responseModels) {
5152
+ if (allModels.has(name)) continue;
5153
+ const resolved = resolveInterfaceName(name, ctx);
5154
+ if (!usedWireTypes.has(resolved)) continue;
5155
+ const wireName = wireInterfaceName(resolved);
5156
+ if (importedTypeNames.has(wireName)) continue;
5157
+ const modelServiceDir = resolveDir(modelToService.get(name));
5158
+ const relPath = modelServiceDir === serviceDir ? `./interfaces/${fileName$3(name)}.interface` : `../${modelServiceDir}/interfaces/${fileName$3(name)}.interface`;
5159
+ lines.push(`import type { ${wireName} } from '${relPath}';`);
5160
+ importedTypeNames.add(wireName);
5161
+ }
4957
5162
  for (const name of baselineResponseTypes) {
4958
5163
  if (importedTypeNames.has(name)) continue;
4959
5164
  const wireName = wireInterfaceName(name);
@@ -5017,47 +5222,60 @@ function generateResourceClass(service, ctx) {
5017
5222
  }
5018
5223
  }
5019
5224
  lines.push("");
5020
- for (const { op, plan, method } of plans) if (plan.isPaginated) {
5021
- const extraParams = op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name));
5022
- if (extraParams.length > 0) {
5023
- const optionsName = paginatedOptionsName(method, resolvedName);
5024
- if (extraParams.some((p) => fieldName$6(p.name) !== wireFieldName(p.name))) {
5025
- const serializerName = `serialize${optionsName}`;
5026
- lines.push(`const ${serializerName} = (options: ${optionsName}): PaginationOptions => {`);
5027
- lines.push(" const wire: Record<string, unknown> = {");
5028
- for (const p of PAGINATION_PARAM_NAMES) lines.push(` ${p}: options.${p},`);
5029
- lines.push(" };");
5030
- for (const param of extraParams) {
5031
- const camel = fieldName$6(param.name);
5032
- const snake = wireFieldName(param.name);
5033
- lines.push(` if (options.${camel} !== undefined) wire.${snake} = options.${camel};`);
5225
+ for (const { op, plan, method } of plans) {
5226
+ const resolved = lookupResolved(op, resolvedLookup);
5227
+ const optionInfo = optionsObjectInfo(service, method, op, plan, ctx, baselineMethodFor$1(service, method, ctx), resolved);
5228
+ if (plan.isPaginated) {
5229
+ const extraParams = op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name));
5230
+ if (extraParams.length > 0) {
5231
+ const optionsName = optionInfo?.type ?? paginatedOptionsName(method, resolvedName);
5232
+ if (extraParams.some((p) => fieldName$6(p.name) !== wireFieldName(p.name))) {
5233
+ const serializerName = `serialize${optionsName}`;
5234
+ lines.push(`const ${serializerName} = (options: ${optionsName}): PaginationOptions => {`);
5235
+ lines.push(" const wire: Record<string, unknown> = {");
5236
+ for (const p of PAGINATION_PARAM_NAMES) lines.push(` ${p}: options.${p},`);
5237
+ lines.push(" };");
5238
+ for (const param of extraParams) {
5239
+ const camel = fieldName$6(param.name);
5240
+ const snake = wireFieldName(param.name);
5241
+ lines.push(` if (options.${camel} !== undefined) wire.${snake} = options.${camel};`);
5242
+ }
5243
+ lines.push(" return wire as PaginationOptions;");
5244
+ lines.push("};");
5245
+ lines.push("");
5034
5246
  }
5035
- lines.push(" return wire as PaginationOptions;");
5036
- lines.push("};");
5037
- lines.push("");
5038
5247
  }
5039
- }
5040
- } else if (!plan.isPaginated && !plan.hasBody && !plan.isDelete && op.queryParams.length > 0) {
5041
- const resolved = lookupResolved(op, resolvedLookup);
5042
- const opHiddenParams = new Set([...Object.keys(getOpDefaults(resolved)), ...getOpInferFromClient(resolved)]);
5043
- const visibleParams = op.queryParams.filter((p) => !opHiddenParams.has(p.name));
5044
- if (visibleParams.length > 0) {
5045
- const optionsName = toPascalCase(method) + "Options";
5046
- lines.push(`export interface ${optionsName} {`);
5047
- for (const param of visibleParams) {
5048
- const opt = !param.required ? "?" : "";
5049
- if (param.description || param.deprecated) {
5050
- const parts = [];
5051
- if (param.description) parts.push(param.description);
5052
- if (param.deprecated) parts.push("@deprecated");
5053
- lines.push(...docComment$2(parts.join("\n"), 2));
5248
+ } else if (!optionInfo && !plan.isPaginated && !plan.hasBody && !plan.isDelete && op.queryParams.length > 0) {
5249
+ const resolved = lookupResolved(op, resolvedLookup);
5250
+ const opHiddenParams = new Set([...Object.keys(getOpDefaults(resolved)), ...getOpInferFromClient(resolved)]);
5251
+ const visibleParams = op.queryParams.filter((p) => !opHiddenParams.has(p.name));
5252
+ if (visibleParams.length > 0) {
5253
+ const optionsName = toPascalCase(method) + "Options";
5254
+ lines.push(`export interface ${optionsName} {`);
5255
+ for (const param of visibleParams) {
5256
+ const opt = !param.required ? "?" : "";
5257
+ if (param.description || param.deprecated) {
5258
+ const parts = [];
5259
+ if (param.description) parts.push(param.description);
5260
+ if (param.deprecated) parts.push("@deprecated");
5261
+ lines.push(...docComment$2(parts.join("\n"), 2));
5262
+ }
5263
+ lines.push(` ${fieldName$6(param.name)}${opt}: ${mapParamType(param.type, specEnumNames)};`);
5054
5264
  }
5055
- lines.push(` ${fieldName$6(param.name)}${opt}: ${mapParamType(param.type, specEnumNames)};`);
5265
+ lines.push("}");
5266
+ lines.push("");
5056
5267
  }
5057
- lines.push("}");
5058
- lines.push("");
5059
5268
  }
5060
5269
  }
5270
+ if (shouldEmitVaultCryptoHelpers) {
5271
+ lines.push("export interface VaultEncryptedData {");
5272
+ lines.push(" ciphertext: string;");
5273
+ lines.push(" encryptedKeys: string;");
5274
+ lines.push(" iv: string;");
5275
+ lines.push(" tag: string;");
5276
+ lines.push("}");
5277
+ lines.push("");
5278
+ }
5061
5279
  if (service.description) lines.push(...docComment$2(service.description));
5062
5280
  lines.push(`export class ${serviceClass} {`);
5063
5281
  lines.push(" constructor(private readonly workos: WorkOS) {}");
@@ -5067,6 +5285,10 @@ function generateResourceClass(service, ctx) {
5067
5285
  lines.push(...renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames, resolved));
5068
5286
  if (resolved?.wrappers && resolved.wrappers.length > 0) lines.push(...generateWrapperMethods$5(resolved, ctx));
5069
5287
  }
5288
+ if (shouldEmitVaultCryptoHelpers) {
5289
+ lines.push("");
5290
+ lines.push(...renderVaultCryptoMethods());
5291
+ }
5070
5292
  lines.push("}");
5071
5293
  return {
5072
5294
  path: resourcePath,
@@ -5074,6 +5296,51 @@ function generateResourceClass(service, ctx) {
5074
5296
  skipIfExists: true
5075
5297
  };
5076
5298
  }
5299
+ function renderVaultCryptoMethods() {
5300
+ return [
5301
+ " async encrypt(",
5302
+ " plaintext: string,",
5303
+ " context: Record<string, string>,",
5304
+ " aad?: string,",
5305
+ " ): Promise<VaultEncryptedData> {",
5306
+ " const dataKeyPair = await this.createDataKey({ context });",
5307
+ " const encodedPlaintext = new TextEncoder().encode(plaintext);",
5308
+ " const encodedAad = aad ? new TextEncoder().encode(aad) : undefined;",
5309
+ " const encrypted = await this.workos.getCryptoProvider().encrypt(",
5310
+ " encodedPlaintext,",
5311
+ " base64ToUint8Array(dataKeyPair.dataKey.key),",
5312
+ " undefined,",
5313
+ " encodedAad,",
5314
+ " );",
5315
+ "",
5316
+ " return {",
5317
+ " ciphertext: uint8ArrayToBase64(encrypted.ciphertext),",
5318
+ " encryptedKeys: dataKeyPair.encryptedKeys,",
5319
+ " iv: uint8ArrayToBase64(encrypted.iv),",
5320
+ " tag: uint8ArrayToBase64(encrypted.tag),",
5321
+ " };",
5322
+ " }",
5323
+ "",
5324
+ " async decrypt(",
5325
+ " encryptedData: VaultEncryptedData,",
5326
+ " aad?: string,",
5327
+ " ): Promise<string> {",
5328
+ " const dataKey = await this.decryptDataKey({",
5329
+ " keys: encryptedData.encryptedKeys,",
5330
+ " });",
5331
+ " const encodedAad = aad ? new TextEncoder().encode(aad) : undefined;",
5332
+ " const decrypted = await this.workos.getCryptoProvider().decrypt(",
5333
+ " base64ToUint8Array(encryptedData.ciphertext),",
5334
+ " base64ToUint8Array(dataKey.key),",
5335
+ " base64ToUint8Array(encryptedData.iv),",
5336
+ " base64ToUint8Array(encryptedData.tag),",
5337
+ " encodedAad,",
5338
+ " );",
5339
+ "",
5340
+ " return new TextDecoder().decode(decrypted);",
5341
+ " }"
5342
+ ];
5343
+ }
5077
5344
  function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames, resolvedOp) {
5078
5345
  const lines = [];
5079
5346
  const responseModel = plan.responseModelName ? resolveInterfaceName(plan.responseModelName, ctx) : null;
@@ -5081,9 +5348,11 @@ function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames,
5081
5348
  const hiddenParams = new Set([...Object.keys(getOpDefaults(resolvedOp)), ...getOpInferFromClient(resolvedOp)]);
5082
5349
  const httpKey = `${op.httpMethod.toUpperCase()} ${op.path}`;
5083
5350
  const baselineClassMethod = baselineMethodFor$1(service, method, ctx);
5351
+ const optionInfo = optionsObjectInfo(service, method, op, plan, ctx, baselineClassMethod, resolvedOp);
5084
5352
  const overlayMethod = ctx.overlayLookup?.methodByOperation?.get(httpKey) ?? baselineClassMethod;
5085
5353
  let validParamNames = null;
5086
- if (overlayMethod) validParamNames = new Set(overlayMethod.params.map((p) => p.name));
5354
+ if (optionInfo) validParamNames = new Set(["options"]);
5355
+ else if (overlayMethod) validParamNames = new Set(overlayMethod.params.map((p) => p.name));
5087
5356
  else {
5088
5357
  const actualParams = /* @__PURE__ */ new Set();
5089
5358
  for (const p of op.pathParams) actualParams.add(fieldName$6(p.name));
@@ -5219,7 +5488,7 @@ function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames,
5219
5488
  }
5220
5489
  }
5221
5490
  }
5222
- if (renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelMap, specEnumNames, baselineClassMethod)) return lines;
5491
+ if (renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelMap, specEnumNames, baselineClassMethod, resolvedOp)) return lines;
5223
5492
  const preDecisionCount = lines.length;
5224
5493
  if (plan.isPaginated && op.pagination && op.httpMethod === "get") {
5225
5494
  let paginatedItemRawName = op.pagination.itemType.kind === "model" ? op.pagination.itemType.name : null;
@@ -5246,12 +5515,17 @@ function renderMethod$2(op, plan, method, service, ctx, modelMap, specEnumNames,
5246
5515
  }
5247
5516
  return lines;
5248
5517
  }
5249
- function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelMap, specEnumNames, baselineMethod) {
5250
- const optionParam = optionsObjectParam$1(baselineMethod);
5518
+ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelMap, specEnumNames, baselineMethod, resolvedOp) {
5519
+ const optionParam = optionsObjectInfo(service, method, op, plan, ctx, baselineMethod, resolvedOp);
5251
5520
  if (!optionParam) return false;
5252
5521
  const responseModel = plan.responseModelName ? resolveInterfaceName(plan.responseModelName, ctx) : null;
5253
5522
  const pathBindings = buildOptionsObjectPathBindings(op, optionParam.type, ctx);
5254
5523
  const pathStr = buildPathStr(op, buildOptionsObjectPathParamMap(op, optionParam.type, ctx));
5524
+ const override = operationOverrideFor$1(ctx, op);
5525
+ const bodyFieldMap = override?.bodyFieldMap;
5526
+ const visibleQueryParams = visibleQueryParamsForOptions(op, plan, resolvedOp);
5527
+ const queryBindings = visibleQueryParams.map((param) => fieldName$6(param.name));
5528
+ const queryOptionsArg = visibleQueryParams.length > 0 || Object.keys(getOpDefaults(resolvedOp)).length > 0 || getOpInferFromClient(resolvedOp).length > 0 ? `, { query: ${renderQueryExprWithOptions(visibleQueryParams, optionParam.optional, resolvedOp)} }` : "";
5255
5529
  if (plan.isPaginated && op.pagination && op.httpMethod === "get") {
5256
5530
  let itemRawName = op.pagination.itemType.kind === "model" ? op.pagination.itemType.name : null;
5257
5531
  if (itemRawName) {
@@ -5265,14 +5539,15 @@ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelM
5265
5539
  if (!itemType) return false;
5266
5540
  const wireType = wireInterfaceName(itemType);
5267
5541
  const returnType = preferredBaselineReturnType(ctx, baselineMethod?.returnType) ?? `Promise<AutoPaginatable<${itemType}>>`;
5268
- lines.push(` async ${method}(options: ${optionParam.type}): ${returnType} {`);
5542
+ const listOptionsExpr = op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name)).some((p) => fieldName$6(p.name) !== wireFieldName(p.name)) ? `options ? serialize${optionParam.type}(options) : undefined` : "paginationOptions";
5543
+ lines.push(` async ${method}(${renderOptionsParam(optionParam)}): ${returnType} {`);
5269
5544
  renderOptionsObjectDestructure(lines, pathBindings, "paginationOptions");
5270
5545
  lines.push(` return new AutoPaginatable(`);
5271
5546
  lines.push(` await fetchAndDeserialize<${wireType}, ${itemType}>(`);
5272
5547
  lines.push(` this.workos,`);
5273
5548
  lines.push(` ${pathStr},`);
5274
5549
  lines.push(` deserialize${itemType},`);
5275
- lines.push(` paginationOptions,`);
5550
+ lines.push(` ${listOptionsExpr},`);
5276
5551
  lines.push(` ),`);
5277
5552
  lines.push(` (params) =>`);
5278
5553
  lines.push(` fetchAndDeserialize<${wireType}, ${itemType}>(`);
@@ -5287,9 +5562,9 @@ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelM
5287
5562
  return true;
5288
5563
  }
5289
5564
  if (plan.isDelete && !plan.hasBody) {
5290
- lines.push(` async ${method}(options: ${optionParam.type}): Promise<void> {`);
5565
+ lines.push(` async ${method}(${renderOptionsParam(optionParam)}): Promise<void> {`);
5291
5566
  renderOptionsObjectDestructure(lines, pathBindings);
5292
- lines.push(` await this.workos.delete(${pathStr});`);
5567
+ lines.push(` await this.workos.delete(${pathStr}${queryOptionsArg});`);
5293
5568
  lines.push(" }");
5294
5569
  return true;
5295
5570
  }
@@ -5312,40 +5587,56 @@ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelM
5312
5587
  entityType = requestType;
5313
5588
  }
5314
5589
  if (plan.isDelete) {
5315
- lines.push(` async ${method}(options: ${optionParam.type}): Promise<void> {`);
5316
- renderOptionsObjectDestructure(lines, pathBindings, "payload");
5317
- lines.push(` await this.workos.deleteWithBody<${entityType}>(${pathStr}, ${bodyExpr});`);
5590
+ lines.push(` async ${method}(${renderOptionsParam(optionParam)}): Promise<void> {`);
5591
+ renderOptionsObjectDestructure(lines, [...pathBindings, ...queryBindings], "payload");
5592
+ const bodyParam = renderOptionsObjectBodyFieldMap(lines, bodyFieldMap);
5593
+ bodyExpr = bodyArgExprWithParam(bodyExpr, bodyParam);
5594
+ lines.push(` await this.workos.deleteWithBody<${entityType}>(${pathStr}, ${bodyExpr}${queryOptionsArg});`);
5318
5595
  lines.push(" }");
5319
5596
  return true;
5320
5597
  }
5321
5598
  if (!responseModel) {
5322
- lines.push(` async ${method}(options: ${optionParam.type}): Promise<void> {`);
5323
- renderOptionsObjectDestructure(lines, pathBindings, "payload");
5324
- lines.push(` await this.workos.${op.httpMethod}<void, ${entityType}>(${pathStr}, ${bodyExpr});`);
5599
+ lines.push(` async ${method}(${renderOptionsParam(optionParam)}): Promise<void> {`);
5600
+ renderOptionsObjectDestructure(lines, [...pathBindings, ...queryBindings], "payload");
5601
+ const bodyParam = renderOptionsObjectBodyFieldMap(lines, bodyFieldMap);
5602
+ bodyExpr = bodyArgExprWithParam(bodyExpr, bodyParam);
5603
+ lines.push(` await this.workos.${op.httpMethod}<void, ${entityType}>(${pathStr}, ${bodyExpr}${queryOptionsArg});`);
5325
5604
  lines.push(" }");
5326
5605
  return true;
5327
5606
  }
5328
5607
  const returnType = plan.isArrayResponse ? `${responseModel}[]` : responseModel;
5608
+ const methodReturnType = override?.returnType ?? `Promise<${returnType}>`;
5329
5609
  const wireType = plan.isArrayResponse ? `${wireInterfaceName(responseModel)}[]` : wireInterfaceName(responseModel);
5330
5610
  const returnExpr = plan.isArrayResponse ? `data.map(deserialize${responseModel})` : `deserialize${responseModel}(data)`;
5331
- lines.push(` async ${method}(options: ${optionParam.type}): Promise<${returnType}> {`);
5332
- renderOptionsObjectDestructure(lines, pathBindings, "payload");
5611
+ const finalReturnExpr = override?.returnDataProperty ? `${returnExpr}.${override.returnDataProperty}` : returnExpr;
5612
+ lines.push(` async ${method}(${renderOptionsParam(optionParam)}): ${methodReturnType} {`);
5613
+ renderOptionsObjectDestructure(lines, [...pathBindings, ...queryBindings], "payload");
5614
+ const bodyParam = renderOptionsObjectBodyFieldMap(lines, bodyFieldMap);
5615
+ bodyExpr = bodyArgExprWithParam(bodyExpr, bodyParam);
5333
5616
  lines.push(` const { data } = await this.workos.${op.httpMethod}<${wireType}, ${entityType}>(`);
5334
5617
  lines.push(` ${pathStr},`);
5335
- lines.push(` ${bodyExpr},`);
5618
+ lines.push(` ${bodyExpr}${queryOptionsArg},`);
5336
5619
  lines.push(" );");
5337
- lines.push(` return ${returnExpr};`);
5620
+ if (override?.returnExpression) {
5621
+ lines.push(` const result = ${returnExpr};`);
5622
+ lines.push(` return ${override.returnExpression};`);
5623
+ } else lines.push(` return ${finalReturnExpr};`);
5338
5624
  lines.push(" }");
5339
5625
  return true;
5340
5626
  }
5341
5627
  if (responseModel) {
5342
5628
  const returnType = plan.isArrayResponse ? `${responseModel}[]` : responseModel;
5629
+ const methodReturnType = override?.returnType ?? `Promise<${returnType}>`;
5343
5630
  const wireType = plan.isArrayResponse ? `${wireInterfaceName(responseModel)}[]` : wireInterfaceName(responseModel);
5344
5631
  const returnExpr = plan.isArrayResponse ? `data.map(deserialize${responseModel})` : `deserialize${responseModel}(data)`;
5345
- lines.push(` async ${method}(options: ${optionParam.type}): Promise<${returnType}> {`);
5632
+ const finalReturnExpr = override?.returnDataProperty ? `${returnExpr}.${override.returnDataProperty}` : returnExpr;
5633
+ lines.push(` async ${method}(${renderOptionsParam(optionParam)}): ${methodReturnType} {`);
5346
5634
  renderOptionsObjectDestructure(lines, pathBindings);
5347
- lines.push(` const { data } = await this.workos.${op.httpMethod}<${wireType}>(${pathStr});`);
5348
- lines.push(` return ${returnExpr};`);
5635
+ lines.push(` const { data } = await this.workos.${op.httpMethod}<${wireType}>(${pathStr}${queryOptionsArg});`);
5636
+ if (override?.returnExpression) {
5637
+ lines.push(` const result = ${returnExpr};`);
5638
+ lines.push(` return ${override.returnExpression};`);
5639
+ } else lines.push(` return ${finalReturnExpr};`);
5349
5640
  lines.push(" }");
5350
5641
  return true;
5351
5642
  }
@@ -5356,6 +5647,18 @@ function renderOptionsObjectDestructure(lines, pathBindings, restName) {
5356
5647
  else if (pathBindings.length > 0) lines.push(` const { ${pathBindings.join(", ")} } = options;`);
5357
5648
  else if (restName) lines.push(` const ${restName} = options;`);
5358
5649
  }
5650
+ function renderOptionsObjectBodyFieldMap(lines, bodyFieldMap) {
5651
+ const entries = Object.entries(bodyFieldMap ?? {});
5652
+ if (entries.length === 0) return "payload";
5653
+ lines.push(" const requestPayload = {");
5654
+ lines.push(" ...payload,");
5655
+ for (const [source, target] of entries) lines.push(` ${target}: payload.${source},`);
5656
+ lines.push(" };");
5657
+ return "requestPayload";
5658
+ }
5659
+ function bodyArgExprWithParam(bodyExpr, paramName) {
5660
+ return paramName === "payload" ? bodyExpr : bodyExpr.replace(/\bpayload\b/g, paramName);
5661
+ }
5359
5662
  function buildOptionsObjectPathBindings(op, optionType, ctx) {
5360
5663
  return op.pathParams.map((param) => resolveOptionsObjectField$1(fieldName$6(param.name), optionType, ctx));
5361
5664
  }
@@ -5634,6 +5937,20 @@ function renderQueryExpr(queryParams) {
5634
5937
  }
5635
5938
  return `options ? { ${parts.join(", ")} } : undefined`;
5636
5939
  }
5940
+ function renderQueryExprWithOptions(queryParams, optional, resolvedOp) {
5941
+ const parts = [];
5942
+ for (const param of queryParams) {
5943
+ const camel = fieldName$6(param.name);
5944
+ const snake = wireFieldName(param.name);
5945
+ const access = optional ? `options?.${camel}` : `options.${camel}`;
5946
+ if (param.required) parts.push(`${snake}: ${access}`);
5947
+ else parts.push(`...(${access} !== undefined && { ${snake}: ${access} })`);
5948
+ }
5949
+ for (const [key, value] of Object.entries(getOpDefaults(resolvedOp))) parts.push(`${key}: ${tsLiteral(value)}`);
5950
+ for (const field of getOpInferFromClient(resolvedOp)) parts.push(`${field}: ${clientFieldExpression$8(field)}`);
5951
+ if (parts.length === 0) return optional ? "options" : "{}";
5952
+ return `{ ${parts.join(", ")} }`;
5953
+ }
5637
5954
  function buildPathStr(op, paramNameMap) {
5638
5955
  return buildNodePathExpression(op.path, paramNameMap);
5639
5956
  }
@@ -6074,6 +6391,17 @@ function generateModels$7(models, ctx, shared) {
6074
6391
  const discriminatedSkip = ctx._discriminatedModelNames;
6075
6392
  const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
6076
6393
  const listMetadataNeeded = collectReferencedListMetadataModels(models, nonPaginatedRefs);
6394
+ for (const originalModel of models) {
6395
+ const model = projectedByName.get(originalModel.name) ?? originalModel;
6396
+ if (!reachableModels.has(model.name)) continue;
6397
+ if (interfaceEligibleModels && !interfaceEligibleModels.has(model.name)) continue;
6398
+ if (!isNodeOwnedService(ctx, modelToService.get(model.name)) && !modelHasNewFields(model, ctx) && !forceGenerate.has(model.name)) continue;
6399
+ const canonicalName = dedup.get(model.name);
6400
+ if (canonicalName) {
6401
+ forceGenerate.add(canonicalName);
6402
+ if (interfaceEligibleModels) interfaceEligibleModels.add(canonicalName);
6403
+ }
6404
+ }
6077
6405
  for (const originalModel of models) {
6078
6406
  const model = projectedByName.get(originalModel.name) ?? originalModel;
6079
6407
  if (!reachableModels.has(model.name)) continue;
@@ -6200,9 +6528,11 @@ function generateModels$7(models, ctx, shared) {
6200
6528
  }
6201
6529
  for (const dep of deps.models) {
6202
6530
  const depName = resolveInterfaceName(dep, ctx);
6203
- const depDir = resolveDir(modelToService.get(dep));
6531
+ const depService = modelToService.get(dep);
6532
+ const depDir = resolveDir(depService);
6533
+ const depIsOwned = isNodeOwnedService(ctx, depService);
6204
6534
  const currentFilePath = `src/${dirName}/interfaces/${fileName$3(model.name)}.interface.ts`;
6205
- const baselineSrc = (ctx.apiSurface?.interfaces?.[depName])?.sourceFile;
6535
+ const baselineSrc = depIsOwned ? void 0 : (ctx.apiSurface?.interfaces?.[depName])?.sourceFile;
6206
6536
  if (baselineSrc === currentFilePath) continue;
6207
6537
  let relPath;
6208
6538
  if (baselineSrc) relPath = relativeImport(currentFilePath, baselineSrc).replace(/\.ts$/, "");
@@ -6385,6 +6715,7 @@ function generateSerializers(models, ctx, shared) {
6385
6715
  const resourceUsage = buildGeneratedResourceModelUsage(models, ctx);
6386
6716
  const serializerEligibleModels = resourceUsage ? expandModelRoots(resourceUsage.serializerRoots, projectedByName) : void 0;
6387
6717
  const responseReachableModels = resourceUsage ? expandModelRoots(resourceUsage.responseRoots, projectedByName) : void 0;
6718
+ const requestReachableModels = resourceUsage ? expandModelRoots(resourceUsage.requestRoots, projectedByName) : void 0;
6388
6719
  const serializerReachable = computeNonEventReachable(ctx.spec.services, models);
6389
6720
  const liveRoot = ctx.targetDir ?? ctx.outputDir;
6390
6721
  if (liveRoot) for (const originalModel of models) {
@@ -6417,6 +6748,17 @@ function generateSerializers(models, ctx, shared) {
6417
6748
  const discriminatedSerializerSkip = ctx._discriminatedModelNames;
6418
6749
  const serializerNonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
6419
6750
  const serializerListMetadataNeeded = collectReferencedListMetadataModels(models, serializerNonPaginatedRefs);
6751
+ for (const originalModel of models) {
6752
+ const model = projectedByName.get(originalModel.name) ?? originalModel;
6753
+ if (!serializerReachable.has(model.name)) continue;
6754
+ if (serializerEligibleModels && !serializerEligibleModels.has(model.name)) continue;
6755
+ if (!isNodeOwnedService(ctx, modelToService.get(model.name)) && !modelHasNewFields(model, ctx) && !forceGenerateSerializer.has(model.name)) continue;
6756
+ const canonicalName = dedup.get(model.name);
6757
+ if (canonicalName) {
6758
+ forceGenerateSerializer.add(canonicalName);
6759
+ if (serializerEligibleModels) serializerEligibleModels.add(canonicalName);
6760
+ }
6761
+ }
6420
6762
  const eligibleModels = [];
6421
6763
  for (const originalModel of models) {
6422
6764
  const model = projectedByName.get(originalModel.name) ?? originalModel;
@@ -6435,7 +6777,7 @@ function generateSerializers(models, ctx, shared) {
6435
6777
  const responseName = wireInterfaceName(domainName);
6436
6778
  const baselineResponse = ctx.apiSurface?.interfaces?.[responseName];
6437
6779
  const baselineDomain = ctx.apiSurface?.interfaces?.[domainName];
6438
- if (shouldSkipSerializeForModel(model, baselineResponse, baselineDomain, dedup, skippedSerializeModels, ctx)) skippedSerializeModels.add(model.name);
6780
+ if (requestReachableModels !== void 0 && !requestReachableModels.has(model.name) || shouldSkipSerializeForModel(model, baselineResponse, baselineDomain, dedup, skippedSerializeModels, ctx)) skippedSerializeModels.add(model.name);
6439
6781
  }
6440
6782
  for (const model of eligibleModels) {
6441
6783
  const service = modelToService.get(model.name);
@@ -6451,20 +6793,24 @@ function generateSerializers(models, ctx, shared) {
6451
6793
  const canonSerializerPath = `src/${canonDir}/serializers/${fileName$3(canonicalName)}.serializer.ts`;
6452
6794
  if (serializerPath === canonSerializerPath) continue;
6453
6795
  if (domainName === canonDomainName) continue;
6454
- const rel = relativeImport(serializerPath, canonSerializerPath);
6455
- const canonSkipSerialize = skippedSerializeModels.has(canonicalName) || skippedSerializeModels.has(model.name);
6456
- const canonSkipDeserialize = responseReachableModels !== void 0 && !responseReachableModels.has(canonicalName) && !responseReachableModels.has(model.name);
6457
- if (canonSkipSerialize && canonSkipDeserialize) continue;
6458
- const parts = [];
6459
- if (!canonSkipDeserialize) parts.push(`deserialize${canonDomainName} as deserialize${domainName}`);
6460
- if (!canonSkipSerialize) parts.push(`serialize${canonDomainName} as serialize${domainName}`);
6461
- const reexportContent = `export { ${parts.join(", ")} } from '${rel}';`;
6462
- files.push({
6463
- path: serializerPath,
6464
- content: reexportContent,
6465
- overwriteExisting: true
6466
- });
6467
- continue;
6796
+ const aliasNeedsDeserialize = responseReachableModels === void 0 || responseReachableModels.has(model.name);
6797
+ const canonicalHasDeserialize = responseReachableModels === void 0 || responseReachableModels.has(canonicalName);
6798
+ if (!aliasNeedsDeserialize || canonicalHasDeserialize) {
6799
+ const rel = relativeImport(serializerPath, canonSerializerPath);
6800
+ const canonSkipSerialize = skippedSerializeModels.has(canonicalName) || skippedSerializeModels.has(model.name);
6801
+ const canonSkipDeserialize = responseReachableModels !== void 0 && !responseReachableModels.has(canonicalName) && !responseReachableModels.has(model.name);
6802
+ if (canonSkipSerialize && canonSkipDeserialize) continue;
6803
+ const parts = [];
6804
+ if (!canonSkipDeserialize) parts.push(`deserialize${canonDomainName} as deserialize${domainName}`);
6805
+ if (!canonSkipSerialize) parts.push(`serialize${canonDomainName} as serialize${domainName}`);
6806
+ const reexportContent = `export { ${parts.join(", ")} } from '${rel}';`;
6807
+ files.push({
6808
+ path: serializerPath,
6809
+ content: reexportContent,
6810
+ overwriteExisting: true
6811
+ });
6812
+ continue;
6813
+ }
6468
6814
  }
6469
6815
  const dirName = resolveDir(service);
6470
6816
  const isDedupCanonical = [...dedup.values()].includes(model.name);
@@ -6504,7 +6850,7 @@ function generateSerializers(models, ctx, shared) {
6504
6850
  }
6505
6851
  const liveRootForBarrel = ctx.outputDir ?? ctx.targetDir;
6506
6852
  for (const [dir, stems] of serializersByDir) {
6507
- if (liveRootForBarrel) {
6853
+ if (liveRootForBarrel && !isNodeOwnedService(ctx, dir)) {
6508
6854
  const serializersDir = path.join(liveRootForBarrel, "src", dir, "serializers");
6509
6855
  try {
6510
6856
  for (const entry of fs.readdirSync(serializersDir)) {
@@ -6750,6 +7096,25 @@ function generateWorkOSClient$1(spec, ctx) {
6750
7096
  skipIfExists: true
6751
7097
  };
6752
7098
  }
7099
+ function sourceFileForExportedType(ctx, typeName) {
7100
+ return (ctx.apiSurface?.interfaces?.[typeName])?.sourceFile ?? (ctx.apiSurface?.typeAliases?.[typeName])?.sourceFile ?? (ctx.apiSurface?.enums?.[typeName])?.sourceFile ?? liveSurfaceInterfacePath(typeName);
7101
+ }
7102
+ function exportedNamesForSource(ctx, sourceFile) {
7103
+ const names = /* @__PURE__ */ new Set();
7104
+ const addNamesFromSurface = (items) => {
7105
+ if (!items) return;
7106
+ for (const [name, item] of Object.entries(items)) if (item.sourceFile === sourceFile) names.add(name);
7107
+ };
7108
+ addNamesFromSurface(ctx.apiSurface?.interfaces);
7109
+ addNamesFromSurface(ctx.apiSurface?.typeAliases);
7110
+ addNamesFromSurface(ctx.apiSurface?.enums);
7111
+ const rootDir = ctx.targetDir ?? ctx.outputDir;
7112
+ if (rootDir) try {
7113
+ const content = fs.readFileSync(path.join(rootDir, sourceFile), "utf-8");
7114
+ for (const match of content.matchAll(/export\s+(?:interface|type|enum|class|const|function)\s+(\w+)/g)) names.add(match[1]);
7115
+ } catch {}
7116
+ return [...names];
7117
+ }
6753
7118
  /**
6754
7119
  * Generate per-service barrel files (interfaces/index.ts) that re-export
6755
7120
  * all interface and enum files for each service directory. This reduces
@@ -6762,11 +7127,14 @@ function generateServiceBarrels(spec, ctx) {
6762
7127
  const enumToService = assignEnumsToServices(spec.enums, spec.services, spec.models, ctx);
6763
7128
  const dirExports = /* @__PURE__ */ new Map();
6764
7129
  const dirSymbols = /* @__PURE__ */ new Map();
7130
+ const ownedDirNames = /* @__PURE__ */ new Set();
7131
+ for (const service of spec.services) if (isNodeOwnedService(ctx, service.name)) ownedDirNames.add(resolveDir(service.name));
6765
7132
  const dirSymbolsFromBaseline = /* @__PURE__ */ new Map();
6766
7133
  const seedFromBaseline = (sourceFile, name) => {
6767
7134
  const match = sourceFile.match(/^src\/([^/]+)\/interfaces\/(.+)\.ts$/);
6768
7135
  if (!match) return;
6769
7136
  const dirName = match[1];
7137
+ if (ownedDirNames.has(dirName)) return;
6770
7138
  const fileStem = match[2];
6771
7139
  if (!dirSymbols.has(dirName)) dirSymbols.set(dirName, /* @__PURE__ */ new Set());
6772
7140
  dirSymbols.get(dirName).add(name);
@@ -6819,9 +7187,63 @@ function generateServiceBarrels(spec, ctx) {
6819
7187
  globalExistingSymbols.add(enumDef.name);
6820
7188
  dirExports.get(dirName).push(`export * from './${fileName$3(enumDef.name)}.interface';`);
6821
7189
  }
7190
+ const overrideWildcardSources = /* @__PURE__ */ new Set();
7191
+ const overrideNamedExports = /* @__PURE__ */ new Set();
7192
+ const addOverrideTypeExport = (typeName) => {
7193
+ if (!typeName) return;
7194
+ const sourceFile = sourceFileForExportedType(ctx, typeName);
7195
+ if (!sourceFile) return;
7196
+ const match = sourceFile?.match(/^src\/([^/]+)\/interfaces\/(.+)\.ts$/);
7197
+ if (!match) return;
7198
+ const dirName = match[1];
7199
+ const stem = match[2].replace(/\.ts$/, "");
7200
+ if (!dirExports.has(dirName)) {
7201
+ dirExports.set(dirName, []);
7202
+ if (!dirSymbols.has(dirName)) dirSymbols.set(dirName, /* @__PURE__ */ new Set());
7203
+ }
7204
+ const symbols = dirSymbols.get(dirName);
7205
+ const sourceKey = `${dirName}/${stem}`;
7206
+ if (overrideWildcardSources.has(sourceKey)) return;
7207
+ const sourceNames = exportedNamesForSource(ctx, sourceFile);
7208
+ if (sourceNames.some((name) => name !== typeName && globalExistingSymbols.has(name))) {
7209
+ const exportKey = `${sourceKey}:${typeName}`;
7210
+ if (overrideNamedExports.has(exportKey)) return;
7211
+ dirExports.get(dirName).push(`export type { ${typeName} } from './${stem}';`);
7212
+ overrideNamedExports.add(exportKey);
7213
+ symbols.add(typeName);
7214
+ globalExistingSymbols.add(typeName);
7215
+ return;
7216
+ }
7217
+ dirExports.get(dirName).push(`export * from './${stem}';`);
7218
+ overrideWildcardSources.add(sourceKey);
7219
+ const namesToRegister = sourceNames.length > 0 ? sourceNames : [typeName];
7220
+ for (const name of namesToRegister) {
7221
+ symbols.add(name);
7222
+ globalExistingSymbols.add(name);
7223
+ }
7224
+ };
7225
+ for (const override of Object.values(nodeOptions(ctx).operationOverrides ?? {})) {
7226
+ addOverrideTypeExport(override.optionsType);
7227
+ for (const typeName of override.returnTypeImports ?? []) addOverrideTypeExport(typeName);
7228
+ }
7229
+ const generatedOptionExports = ctx._nodeGeneratedOptionInterfaceExports;
7230
+ for (const [dirName, options] of generatedOptionExports ?? []) {
7231
+ if (!dirExports.has(dirName)) {
7232
+ dirExports.set(dirName, []);
7233
+ if (!dirSymbols.has(dirName)) dirSymbols.set(dirName, /* @__PURE__ */ new Set());
7234
+ }
7235
+ const symbols = dirSymbols.get(dirName);
7236
+ for (const { stem, typeName } of options) {
7237
+ if (globalExistingSymbols.has(typeName)) continue;
7238
+ symbols.add(typeName);
7239
+ globalExistingSymbols.add(typeName);
7240
+ dirExports.get(dirName).push(`export * from './${stem}';`);
7241
+ }
7242
+ }
6822
7243
  for (const [dirName, exports] of dirExports) {
6823
7244
  const exportSet = new Set(exports);
6824
- if (ctx.apiSurface) {
7245
+ const isDirOwned = ownedDirNames.has(dirName);
7246
+ if (ctx.apiSurface && !isDirOwned) {
6825
7247
  const addBaselineExports = (items) => {
6826
7248
  if (!items) return;
6827
7249
  for (const item of Object.values(items)) {
@@ -6834,7 +7256,7 @@ function generateServiceBarrels(spec, ctx) {
6834
7256
  addBaselineExports(ctx.apiSurface.interfaces);
6835
7257
  addBaselineExports(ctx.apiSurface.typeAliases);
6836
7258
  addBaselineExports(ctx.apiSurface.enums);
6837
- if (ctx.targetDir) {
7259
+ if (ctx.targetDir && !isDirOwned) {
6838
7260
  const interfacesDir = path.join(ctx.targetDir, "src", dirName, "interfaces");
6839
7261
  try {
6840
7262
  const barrelPath = path.join(interfacesDir, "index.ts");
@@ -7088,6 +7510,9 @@ function serverConstName(description) {
7088
7510
  }
7089
7511
  //#endregion
7090
7512
  //#region src/node/tests.ts
7513
+ function operationOverrideFor(ctx, op) {
7514
+ return nodeOptions(ctx).operationOverrides?.[`${op.httpMethod.toUpperCase()} ${op.path}`];
7515
+ }
7091
7516
  function baselineMethodFor(service, method, ctx) {
7092
7517
  const serviceClass = resolveResourceClassName$3(service, ctx);
7093
7518
  return ctx.apiSurface?.classes?.[serviceClass]?.methods?.[method]?.[0];
@@ -7103,6 +7528,34 @@ function optionsObjectParam(method) {
7103
7528
  type: param.type
7104
7529
  };
7105
7530
  }
7531
+ function configuredOptionsMethod(ctx, op) {
7532
+ const optionsType = operationOverrideFor(ctx, op)?.optionsType;
7533
+ if (!optionsType) return void 0;
7534
+ return { params: [{
7535
+ name: "options",
7536
+ type: optionsType,
7537
+ passingStyle: "options_object"
7538
+ }] };
7539
+ }
7540
+ function methodOptionsName(method, serviceClass) {
7541
+ if (method === "list") return `${toPascalCase(serviceClass)}ListOptions`;
7542
+ return `${toPascalCase(method)}Options`;
7543
+ }
7544
+ function hasOptionsInput(op, plan) {
7545
+ return op.pathParams.length > 0 || plan.hasBody || plan.isPaginated || op.queryParams.length > 0;
7546
+ }
7547
+ function optionsMethodFor(service, method, op, plan, ctx) {
7548
+ const baseline = baselineMethodFor(service, method, ctx);
7549
+ if (optionsObjectParam(baseline)) return baseline;
7550
+ const configured = configuredOptionsMethod(ctx, op);
7551
+ if (configured) return configured;
7552
+ if (!hasOptionsInput(op, plan)) return baseline;
7553
+ return { params: [{
7554
+ name: "options",
7555
+ type: methodOptionsName(method, resolveResourceClassName$3(service, ctx)),
7556
+ passingStyle: "options_object"
7557
+ }] };
7558
+ }
7106
7559
  function autoPaginatableItemType(returnType) {
7107
7560
  return returnType?.match(/\bAutoPaginatable<\s*([A-Za-z_$][\w$]*)/)?.[1];
7108
7561
  }
@@ -7212,7 +7665,7 @@ function generateServiceTest$3(service, spec, ctx, modelMap, mountAccessors) {
7212
7665
  lines.push(`describe('${serviceClass}', () => {`);
7213
7666
  lines.push(" beforeEach(() => fetch.resetMocks());");
7214
7667
  for (const { op, plan, method } of plans) {
7215
- const existingMethod = baselineMethodFor(service, method, ctx);
7668
+ const existingMethod = optionsMethodFor(service, method, op, plan, ctx);
7216
7669
  lines.push("");
7217
7670
  lines.push(` describe('${method}', () => {`);
7218
7671
  if (plan.isPaginated) renderPaginatedTest(lines, op, plan, method, serviceProp, modelMap, ctx, entityHelperNames, existingMethod);
@@ -7329,6 +7782,12 @@ function renderBodyTest(lines, op, plan, method, serviceProp, modelMap, ctx, ent
7329
7782
  if (payload) lines.push(` expect(fetchBody()).toEqual(expect.objectContaining(${payload.snakeCaseObj}));`);
7330
7783
  else lines.push(" expect(fetchBody()).toBeDefined();");
7331
7784
  if (isArrayResponse) lines.push(" expect(Array.isArray(result)).toBe(true);");
7785
+ const override = ctx ? operationOverrideFor(ctx, op) : void 0;
7786
+ if (override?.returnExpression || override?.returnDataProperty) {
7787
+ lines.push(" expect(result).toBeDefined();");
7788
+ lines.push(" });");
7789
+ return;
7790
+ }
7332
7791
  const bodyHelperName = ctx ? `expect${resolveInterfaceName(responseModelName, ctx)}` : null;
7333
7792
  if (bodyHelperName && entityHelpers?.has(bodyHelperName)) lines.push(` ${bodyHelperName}(${accessor});`);
7334
7793
  else {
@@ -7358,6 +7817,13 @@ function renderGetTest(lines, op, plan, method, serviceProp, modelMap, ctx, enti
7358
7817
  const expectedPathGet = buildExpectedPath$4(op);
7359
7818
  lines.push(` expect(new URL(String(fetchURL())).pathname).toBe('${expectedPathGet}');`);
7360
7819
  if (isArrayResponse) lines.push(" expect(Array.isArray(result)).toBe(true);");
7820
+ const returnDataProperty = ctx ? operationOverrideFor(ctx, op)?.returnDataProperty : void 0;
7821
+ if (returnDataProperty) {
7822
+ if ((modelMap.get(responseModelName)?.fields.find((field) => fieldName$6(field.name) === returnDataProperty))?.type.kind === "array") lines.push(" expect(Array.isArray(result)).toBe(true);");
7823
+ else lines.push(" expect(result).toBeDefined();");
7824
+ lines.push(" });");
7825
+ return;
7826
+ }
7361
7827
  const helperName = ctx ? `expect${resolveInterfaceName(responseModelName, ctx)}` : null;
7362
7828
  if (helperName && entityHelpers?.has(helperName)) lines.push(` ${helperName}(${accessor});`);
7363
7829
  else {
@@ -7395,12 +7861,37 @@ function buildOptionsObjectTestArg(op, plan, baselineMethod, modelMap, ctx) {
7395
7861
  const optionField = resolveOptionsObjectField(localName, optionParam.type, ctx);
7396
7862
  entries.push(`${optionField}: ${JSON.stringify(pathParamTestValue(param, localName))}`);
7397
7863
  }
7864
+ if (plan.isPaginated) entries.push("order: 'desc'");
7865
+ const queryParams = plan.isPaginated ? op.queryParams.filter((param) => ![
7866
+ "limit",
7867
+ "before",
7868
+ "after",
7869
+ "order"
7870
+ ].includes(param.name)) : op.queryParams;
7871
+ for (const param of queryParams) {
7872
+ const localName = fieldName$6(param.name);
7873
+ const value = fixtureValueForType(param.type, param.name, "Options", modelMap) ?? "'test'";
7874
+ entries.push(`${localName}: ${value}`);
7875
+ }
7398
7876
  if (plan.hasBody) {
7399
7877
  const payload = buildTestPayload(op, modelMap);
7400
- if (payload) entries.push(...objectLiteralEntries(payload.camelCaseObj));
7878
+ if (payload) {
7879
+ const bodyFieldMap = ctx ? operationOverrideFor(ctx, op)?.bodyFieldMap : void 0;
7880
+ entries.push(...mapBodyOptionEntries(objectLiteralEntries(payload.camelCaseObj), bodyFieldMap));
7881
+ } else if (entries.length === 0) return "({} as any)";
7401
7882
  }
7402
7883
  return `{ ${entries.join(", ")} }`;
7403
7884
  }
7885
+ function mapBodyOptionEntries(entries, bodyFieldMap) {
7886
+ const reverseMap = new Map(Object.entries(bodyFieldMap ?? {}).map(([source, target]) => [target, source]));
7887
+ if (reverseMap.size === 0) return entries;
7888
+ return entries.map((entry) => {
7889
+ const match = entry.match(/^([A-Za-z_$][\w$]*)\s*:/);
7890
+ if (!match) return entry;
7891
+ const source = reverseMap.get(match[1]);
7892
+ return source ? entry.replace(match[1], source) : entry;
7893
+ });
7894
+ }
7404
7895
  function objectLiteralEntries(literal) {
7405
7896
  const trimmed = literal.trim();
7406
7897
  if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return [];
@@ -7476,7 +7967,8 @@ function buildFieldAssertions(model, accessor, modelMap) {
7476
7967
  for (const field of model.fields) {
7477
7968
  if (!field.required) continue;
7478
7969
  const domainField = fieldName$6(field.name);
7479
- const fieldAccessor = isDateTimeFieldType(field.type) ? `${accessor}.${domainField}.toISOString()` : `${accessor}.${domainField}`;
7970
+ const isDateTime = isDateTimeFieldType(field.type);
7971
+ const fieldAccessor = isDateTime ? `${accessor}.${domainField}.toISOString()` : `${accessor}.${domainField}`;
7480
7972
  if (field.example !== void 0) {
7481
7973
  if (field.example === null) {
7482
7974
  assertions.push(`expect(${accessor}.${domainField}).toBeNull();`);
@@ -7484,7 +7976,7 @@ function buildFieldAssertions(model, accessor, modelMap) {
7484
7976
  }
7485
7977
  if (typeof field.example === "object") assertions.push(`expect(${accessor}.${domainField}).toEqual(${JSON.stringify(field.example)});`);
7486
7978
  else {
7487
- const exampleLiteral = typeof field.example === "string" ? `'${field.example}'` : String(field.example);
7979
+ const exampleLiteral = isDateTime && typeof field.example === "string" ? `'${new Date(field.example).toISOString()}'` : typeof field.example === "string" ? `'${field.example}'` : String(field.example);
7488
7980
  assertions.push(`expect(${fieldAccessor}).toBe(${exampleLiteral});`);
7489
7981
  }
7490
7982
  continue;
@@ -7513,7 +8005,7 @@ function buildFieldAssertions(model, accessor, modelMap) {
7513
8005
  */
7514
8006
  function fixtureValueForType(ref, name, modelName, modelMap, wire) {
7515
8007
  switch (ref.kind) {
7516
- case "primitive": return fixtureValueForPrimitive(ref.type, ref.format, name, modelName);
8008
+ case "primitive": return fixtureValueForPrimitive(ref.type, ref.format, name, modelName, wire);
7517
8009
  case "literal": return typeof ref.value === "string" ? `'${ref.value}'` : String(ref.value);
7518
8010
  case "enum":
7519
8011
  if (ref.values?.length) {
@@ -7543,10 +8035,10 @@ function fixtureValueForType(ref, name, modelName, modelMap, wire) {
7543
8035
  default: return null;
7544
8036
  }
7545
8037
  }
7546
- function fixtureValueForPrimitive(type, format, name, modelName) {
8038
+ function fixtureValueForPrimitive(type, format, name, modelName, wire) {
7547
8039
  switch (type) {
7548
8040
  case "string":
7549
- if (format === "date-time") return "'2023-01-01T00:00:00.000Z'";
8041
+ if (format === "date-time") return wire ? "'2023-01-01T00:00:00.000Z'" : "new Date('2023-01-01T00:00:00.000Z')";
7550
8042
  if (format === "date") return "'2023-01-01'";
7551
8043
  if (format === "uuid") return "'00000000-0000-0000-0000-000000000000'";
7552
8044
  if (name === "id") return `'${ID_PREFIXES$4[modelName] ?? ""}01234'`;
@@ -7771,11 +8263,14 @@ function detectDiscriminatedShape(modelName, rawSchemas) {
7771
8263
  };
7772
8264
  }).filter((v) => v !== null);
7773
8265
  if (variants.length !== flattenedVariants.length) return null;
8266
+ const baseFields = baseObject ? collectObjectFields(baseObject, modelName) : [];
8267
+ const discriminatorDescription = flattenedVariants[0].alwaysProperties.get(discProp)?.description;
7774
8268
  return {
7775
8269
  modelName,
7776
- baseFields: baseObject ? collectObjectFields(baseObject, modelName) : [],
8270
+ baseFields,
7777
8271
  discriminatorProperty: discProp,
7778
8272
  discriminatorPropertyDomain: toCamelCase(discProp),
8273
+ discriminatorDescription,
7779
8274
  variants
7780
8275
  };
7781
8276
  }
@@ -8052,6 +8547,7 @@ function buildInterfaceBody(name, shape, variant, isWire) {
8052
8547
  lines.push(`export interface ${name} {`);
8053
8548
  for (const field of shape.baseFields) pushFieldLine(lines, field, isWire);
8054
8549
  const discKey = isWire ? shape.discriminatorProperty : shape.discriminatorPropertyDomain;
8550
+ if (shape.discriminatorDescription) lines.push(` /** ${shape.discriminatorDescription} */`);
8055
8551
  lines.push(` ${discKey}: '${variant.discriminatorValue}';`);
8056
8552
  for (const field of variant.fields) pushFieldLine(lines, field, isWire);
8057
8553
  lines.push("}");
@@ -8180,20 +8676,6 @@ function scalarModelDepName(field) {
8180
8676
  }
8181
8677
  //#endregion
8182
8678
  //#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
8679
  const contextCache = /* @__PURE__ */ new WeakMap();
8198
8680
  function operationKey(resolved) {
8199
8681
  return `${resolved.operation.httpMethod.toUpperCase()} ${resolved.operation.path}`;
@@ -8245,9 +8727,10 @@ function withNodeOperationOverrides(ctx) {
8245
8727
  contextCache.set(ctx, next);
8246
8728
  return next;
8247
8729
  }
8730
+ const configOverrides = nodeOptions(ctx).operationOverrides ?? {};
8248
8731
  let opsChanged = false;
8249
8732
  const nextResolved = resolvedOperations.map((resolved) => {
8250
- const override = OPERATION_OVERRIDES[operationKey(resolved)];
8733
+ const override = configOverrides[operationKey(resolved)];
8251
8734
  if (!override) return resolved;
8252
8735
  const methodName = override.methodName ?? resolved.methodName;
8253
8736
  const mountOn = override.mountOn ?? resolved.mountOn;
@@ -8475,20 +8958,48 @@ function isOwnedPath(relPath, policy) {
8475
8958
  const dir = topLevelDir(relPath);
8476
8959
  return dir !== void 0 && policy.ownedServiceDirs.has(dir);
8477
8960
  }
8961
+ function extractRelativeImportPaths(content, fromPath) {
8962
+ const dir = path$1.dirname(fromPath);
8963
+ const paths = [];
8964
+ const re = /from\s+['"](\.[^'"]+)['"]/g;
8965
+ let match;
8966
+ while ((match = re.exec(content)) !== null) paths.push(path$1.normalize(path$1.join(dir, match[1])) + ".ts");
8967
+ return paths;
8968
+ }
8478
8969
  function applyLiveSurface(files, ctx, surface) {
8479
8970
  const out = [];
8480
8971
  const policy = buildLiveSurfacePolicy(ctx, surface);
8972
+ const filesByPath = new Map(files.map((f) => [f.path, f]));
8973
+ const dependencyAllowedPaths = /* @__PURE__ */ new Set();
8974
+ const queue = [];
8975
+ for (const f of files) {
8976
+ if (f.integrateTarget === false) continue;
8977
+ if (!canCreateNewPath(f.path, policy)) continue;
8978
+ for (const importPath of extractRelativeImportPaths(f.content, f.path)) if (filesByPath.has(importPath) && !canCreateNewPath(importPath, policy) && !dependencyAllowedPaths.has(importPath)) {
8979
+ dependencyAllowedPaths.add(importPath);
8980
+ queue.push(importPath);
8981
+ }
8982
+ }
8983
+ while (queue.length > 0) {
8984
+ const relPath = queue.pop();
8985
+ const file = filesByPath.get(relPath);
8986
+ if (!file) continue;
8987
+ for (const importPath of extractRelativeImportPaths(file.content, relPath)) if (filesByPath.has(importPath) && !canCreateNewPath(importPath, policy) && !dependencyAllowedPaths.has(importPath)) {
8988
+ dependencyAllowedPaths.add(importPath);
8989
+ queue.push(importPath);
8990
+ }
8991
+ }
8481
8992
  for (const f of files) {
8482
8993
  const ownedPath = isOwnedPath(f.path, policy);
8483
8994
  if (f.integrateTarget === false) continue;
8484
8995
  if (surface.protectedFiles.has(f.path)) continue;
8485
- if (policy.hasExistingSdk && !policy.managedPaths.has(f.path) && !canCreateNewPath(f.path, policy)) continue;
8996
+ if (policy.hasExistingSdk && !policy.managedPaths.has(f.path) && !canCreateNewPath(f.path, policy) && !dependencyAllowedPaths.has(f.path)) continue;
8486
8997
  if (surface.files.has(f.path) && !surface.autogenFiles.has(f.path) && !ownedPath) continue;
8487
8998
  if (isUserOwnedAfterFirstEmit(f.path)) {
8488
8999
  const dir = topLevelDir(f.path);
8489
9000
  const isAdoptedDir = dir !== void 0 && policy.adoptedServiceDirs.has(dir);
8490
9001
  const isManagedDir = ownedPath || isAdoptedDir;
8491
- if (surface.files.has(f.path) && !surface.autogenFiles.has(f.path)) continue;
9002
+ if (surface.files.has(f.path) && !surface.autogenFiles.has(f.path) && !(ownedPath && policy.regenerateOwnedTests)) continue;
8492
9003
  if (!isManagedDir && !surface.autogenFiles.has(f.path)) continue;
8493
9004
  if (isManagedDir && !policy.regenerateOwnedTests) continue;
8494
9005
  }
@@ -8582,7 +9093,9 @@ const nodeEmitter = {
8582
9093
  const surface = getSurface(nodeCtx);
8583
9094
  const enriched = enrichModelsForNode(models);
8584
9095
  const discPlans = planDiscriminatedModels(enriched, nodeCtx);
8585
- nodeCtx._discriminatedModelNames = new Set(discPlans.keys());
9096
+ const discriminatedNames = new Set(discPlans.keys());
9097
+ nodeCtx._discriminatedModelNames = discriminatedNames;
9098
+ setDiscriminatedModelNames(discriminatedNames);
8586
9099
  const standardFiles = generateModelsAndSerializers(enriched, nodeCtx);
8587
9100
  const discFiles = generateDiscriminatedFiles(discPlans, nodeCtx);
8588
9101
  return applyLiveSurface([...standardFiles, ...discFiles], nodeCtx, surface);
@@ -9195,11 +9708,24 @@ function generateEnums$6(enums, ctx) {
9195
9708
  const canonicalCls = className$5(canonicalName);
9196
9709
  const aliasCls = className$5(enumDef.name);
9197
9710
  const lines = [];
9198
- lines.push("from typing import TypeAlias");
9199
- if (canonicalDir === dirName) lines.push(`from .${fileName$2(canonicalName)} import ${canonicalCls}`);
9200
- else lines.push(`from ${ctx.namespace}.${dirToModule(canonicalDir)}.models.${fileName$2(canonicalName)} import ${canonicalCls}`);
9201
- lines.push("");
9202
- lines.push(`${aliasCls}: TypeAlias = ${canonicalCls}`);
9711
+ if (canonicalDir === dirName) {
9712
+ lines.push("from typing import TypeAlias");
9713
+ lines.push(`from .${fileName$2(canonicalName)} import ${canonicalCls}`);
9714
+ lines.push("");
9715
+ lines.push(`${aliasCls}: TypeAlias = ${canonicalCls}`);
9716
+ } else {
9717
+ const modPath = `${ctx.namespace}.${dirToModule(canonicalDir)}.models.${fileName$2(canonicalName)}`;
9718
+ lines.push("from typing import TYPE_CHECKING");
9719
+ lines.push("");
9720
+ lines.push("if TYPE_CHECKING:");
9721
+ lines.push(` from ${modPath} import ${canonicalCls} as ${aliasCls}`);
9722
+ lines.push("else:");
9723
+ lines.push(" def __getattr__(name: str):");
9724
+ lines.push(` if name == "${aliasCls}":`);
9725
+ lines.push(` from ${modPath} import ${canonicalCls}`);
9726
+ lines.push(` return ${canonicalCls}`);
9727
+ lines.push(" raise AttributeError(f\"module {__name__!r} has no attribute {name!r}\")");
9728
+ }
9203
9729
  lines.push(`__all__ = ["${aliasCls}"]`);
9204
9730
  files.push({
9205
9731
  path: `src/${ctx.namespace}/${dirName}/models/${fileName$2(enumDef.name)}.py`,
@@ -9208,16 +9734,33 @@ function generateEnums$6(enums, ctx) {
9208
9734
  overwriteExisting: true
9209
9735
  });
9210
9736
  for (const aliasName of compatAliases.get(enumDef.name) ?? []) {
9211
- const importLine = canonicalDir === dirName ? `from .${fileName$2(canonicalName)} import ${canonicalCls}` : `from ${ctx.namespace}.${dirToModule(canonicalDir)}.models.${fileName$2(canonicalName)} import ${canonicalCls}`;
9212
- files.push({
9213
- path: `src/${ctx.namespace}/${dirName}/models/${fileName$2(aliasName)}.py`,
9214
- content: [
9215
- "from typing import TypeAlias",
9216
- importLine,
9737
+ let compatContent;
9738
+ if (canonicalDir === dirName) compatContent = [
9739
+ "from typing import TypeAlias",
9740
+ `from .${fileName$2(canonicalName)} import ${canonicalCls}`,
9741
+ "",
9742
+ `${aliasName}: TypeAlias = ${canonicalCls}`,
9743
+ `__all__ = ["${aliasName}"]`
9744
+ ].join("\n");
9745
+ else {
9746
+ const modPath = `${ctx.namespace}.${dirToModule(canonicalDir)}.models.${fileName$2(canonicalName)}`;
9747
+ compatContent = [
9748
+ "from typing import TYPE_CHECKING",
9217
9749
  "",
9218
- `${aliasName}: TypeAlias = ${canonicalCls}`,
9750
+ "if TYPE_CHECKING:",
9751
+ ` from ${modPath} import ${canonicalCls} as ${aliasName}`,
9752
+ "else:",
9753
+ " def __getattr__(name: str):",
9754
+ ` if name == "${aliasName}":`,
9755
+ ` from ${modPath} import ${canonicalCls}`,
9756
+ ` return ${canonicalCls}`,
9757
+ " raise AttributeError(f\"module {__name__!r} has no attribute {name!r}\")",
9219
9758
  `__all__ = ["${aliasName}"]`
9220
- ].join("\n"),
9759
+ ].join("\n");
9760
+ }
9761
+ files.push({
9762
+ path: `src/${ctx.namespace}/${dirName}/models/${fileName$2(aliasName)}.py`,
9763
+ content: compatContent,
9221
9764
  integrateTarget: true,
9222
9765
  overwriteExisting: true
9223
9766
  });
@@ -9391,6 +9934,8 @@ function generateModels$6(models, ctx) {
9391
9934
  const emittedFilePaths = /* @__PURE__ */ new Set();
9392
9935
  const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
9393
9936
  const listMetadataNeeded = collectReferencedListMetadataModels(models, nonPaginatedRefs);
9937
+ const discriminatorNames = /* @__PURE__ */ new Set();
9938
+ for (const m of models) if (m.discriminator) discriminatorNames.add(m.name);
9394
9939
  for (const model of models) {
9395
9940
  if (isListWrapperModel(model) && !nonPaginatedRefs.has(model.name)) continue;
9396
9941
  if (isListMetadataModel(model) && !listMetadataNeeded.has(model.name)) continue;
@@ -9526,7 +10071,7 @@ function generateModels$6(models, ctx) {
9526
10071
  typingImports.add("Dict");
9527
10072
  for (const field of deduplicatedFields) collectTypingImports(field.type, typingImports);
9528
10073
  if (deduplicatedFields.some((f) => isOptionalField(model.name, f, ctx))) typingImports.add("Optional");
9529
- const usesDateTime = deduplicatedFields.some((f) => isDateTimeType(f.type));
10074
+ const usesDateTime = deduplicatedFields.some((f) => isDateTimeType$1(f.type));
9530
10075
  const usesEnum = deps.enums.size > 0;
9531
10076
  lines.push("from __future__ import annotations");
9532
10077
  lines.push("");
@@ -9542,8 +10087,9 @@ function generateModels$6(models, ctx) {
9542
10087
  for (const modelName of [...deps.models].sort()) {
9543
10088
  if (modelName === model.name) continue;
9544
10089
  const modelDir = resolveDir(modelToService.get(modelName));
9545
- if (modelDir === dirName) lines.push(`from .${fileName$2(modelName)} import ${className$5(modelName)}`);
9546
- else lines.push(`from ${ctx.namespace}.${dirToModule(modelDir)}.models.${fileName$2(modelName)} import ${className$5(modelName)}`);
10090
+ const importNames = discriminatorNames.has(modelName) ? `${className$5(modelName)}, ${className$5(modelName)}Variant` : className$5(modelName);
10091
+ if (modelDir === dirName) lines.push(`from .${fileName$2(modelName)} import ${importNames}`);
10092
+ else lines.push(`from ${ctx.namespace}.${dirToModule(modelDir)}.models.${fileName$2(modelName)} import ${importNames}`);
9547
10093
  }
9548
10094
  }
9549
10095
  if (deps.enums.size > 0) for (const enumName of [...deps.enums].sort()) {
@@ -9564,9 +10110,16 @@ function generateModels$6(models, ctx) {
9564
10110
  lines.push("");
9565
10111
  const requiredFields = deduplicatedFields.filter((f) => !isOptionalField(model.name, f, ctx));
9566
10112
  const optionalFields = deduplicatedFields.filter((f) => isOptionalField(model.name, f, ctx));
10113
+ const rewriteDiscriminatorType = (typeStr) => {
10114
+ for (const discName of discriminatorNames) {
10115
+ const quoted = `"${className$5(discName)}"`;
10116
+ if (typeStr.includes(quoted)) typeStr = typeStr.replace(quoted, `"${className$5(discName)}Variant"`);
10117
+ }
10118
+ return typeStr;
10119
+ };
9567
10120
  for (const field of requiredFields) {
9568
10121
  const pyFieldName = fieldName$5(field.name);
9569
- const pyType = resolveModelFieldType(field.type);
10122
+ const pyType = rewriteDiscriminatorType(resolveModelFieldType(field.type));
9570
10123
  if (field.description || field.deprecated) {
9571
10124
  const parts = [];
9572
10125
  if (field.description) parts.push(field.description);
@@ -9577,7 +10130,7 @@ function generateModels$6(models, ctx) {
9577
10130
  }
9578
10131
  for (const field of optionalFields) {
9579
10132
  const pyFieldName = fieldName$5(field.name);
9580
- const pyType = `Optional[${field.type.kind === "nullable" ? resolveModelFieldType(field.type.inner) : resolveModelFieldType(field.type)}]`;
10133
+ const pyType = `Optional[${rewriteDiscriminatorType(field.type.kind === "nullable" ? resolveModelFieldType(field.type.inner) : resolveModelFieldType(field.type))}]`;
9581
10134
  if (field.description || field.deprecated) {
9582
10135
  const parts = [];
9583
10136
  if (field.description) parts.push(field.description);
@@ -9821,12 +10374,12 @@ function pythonLiteralDefault(value) {
9821
10374
  return String(value);
9822
10375
  }
9823
10376
  function resolveModelFieldType(ref) {
9824
- if (ref.kind === "nullable" && isDateTimeType(ref.inner)) return "Optional[datetime]";
9825
- if (isDateTimeType(ref)) return "datetime";
10377
+ if (ref.kind === "nullable" && isDateTimeType$1(ref.inner)) return "Optional[datetime]";
10378
+ if (isDateTimeType$1(ref)) return "datetime";
9826
10379
  return mapTypeRef$6(ref);
9827
10380
  }
9828
- function isDateTimeType(ref) {
9829
- if (ref.kind === "nullable") return isDateTimeType(ref.inner);
10381
+ function isDateTimeType$1(ref) {
10382
+ if (ref.kind === "nullable") return isDateTimeType$1(ref.inner);
9830
10383
  return ref.kind === "primitive" && ref.type === "string" && ref.format === "date-time";
9831
10384
  }
9832
10385
  /**
@@ -9887,7 +10440,7 @@ function renderDiscriminatedUnionPrelude(field, pyFieldName, wireKey, parentClas
9887
10440
  prelude.push(`${indent}if ${rawVar} is None:`);
9888
10441
  prelude.push(`${indent} ${valueVar} = None`);
9889
10442
  prelude.push(`${indent}else:`);
9890
- prelude.push(...dispatchBlock(indent + " "));
10443
+ prelude.push(...dispatchBlock(" "));
9891
10444
  prelude.push(`${indent} ${valueVar} = ${clsVar}.from_dict(${dataVar})`);
9892
10445
  return {
9893
10446
  prelude,
@@ -9895,7 +10448,7 @@ function renderDiscriminatedUnionPrelude(field, pyFieldName, wireKey, parentClas
9895
10448
  };
9896
10449
  }
9897
10450
  function deserializeField(ref, accessor, isRequired, walrusVar = "_v") {
9898
- if (isDateTimeType(ref)) {
10451
+ if (isDateTimeType$1(ref)) {
9899
10452
  if (isRequired) return `_parse_datetime(${accessor})`;
9900
10453
  return `_parse_datetime(${walrusVar}) if (${walrusVar} := ${accessor}) is not None else None`;
9901
10454
  }
@@ -9934,7 +10487,7 @@ function deserializeField(ref, accessor, isRequired, walrusVar = "_v") {
9934
10487
  }
9935
10488
  }
9936
10489
  function serializeField(ref, accessor) {
9937
- if (isDateTimeType(ref)) return `_format_datetime(${accessor})`;
10490
+ if (isDateTimeType$1(ref)) return `_format_datetime(${accessor})`;
9938
10491
  switch (ref.kind) {
9939
10492
  case "model": return `${accessor}.to_dict()`;
9940
10493
  case "enum": return `${accessor}.value if isinstance(${accessor}, Enum) else ${accessor}`;
@@ -10078,7 +10631,8 @@ function emitWrapperMethod$5(lines, resolvedOp, wrapper, ctx, isAsync) {
10078
10631
  else lines.push(` ${pyName}: ${pyType},`);
10079
10632
  }
10080
10633
  lines.push(" request_options: Optional[RequestOptions] = None,");
10081
- const responseType = wrapper.responseModelName ? className$5(wrapper.responseModelName) : "None";
10634
+ const isDiscriminatorResponse = wrapper.responseModelName ? !!ctx.spec.models.find((m) => m.name === wrapper.responseModelName)?.discriminator : false;
10635
+ const responseType = wrapper.responseModelName ? isDiscriminatorResponse ? className$5(wrapper.responseModelName) + "Variant" : className$5(wrapper.responseModelName) : "None";
10082
10636
  lines.push(` ) -> ${responseType}:`);
10083
10637
  lines.push(` """${formatWrapperDescription(wrapper.name)}."""`);
10084
10638
  lines.push(" body: Dict[str, Any] = {");
@@ -10103,7 +10657,19 @@ function emitWrapperMethod$5(lines, resolvedOp, wrapper, ctx, isAsync) {
10103
10657
  const pathExpr = buildPythonPathExpression(op.path);
10104
10658
  const awaitPrefix = isAsync ? "await " : "";
10105
10659
  lines.push("");
10106
- if (wrapper.responseModelName) {
10660
+ if (wrapper.responseModelName && isDiscriminatorResponse) {
10661
+ const variantType = className$5(wrapper.responseModelName) + "Variant";
10662
+ lines.push(` return cast(`);
10663
+ lines.push(` ${variantType},`);
10664
+ lines.push(` ${awaitPrefix}self._client.request(`);
10665
+ lines.push(` method="${op.httpMethod.toUpperCase()}",`);
10666
+ lines.push(` path=${pathExpr},`);
10667
+ lines.push(" body=body,");
10668
+ lines.push(` model=${className$5(wrapper.responseModelName)}, # type: ignore[arg-type] # dispatcher; request only calls from_dict`);
10669
+ lines.push(" request_options=request_options,");
10670
+ lines.push(" )");
10671
+ lines.push(" )");
10672
+ } else if (wrapper.responseModelName) {
10107
10673
  lines.push(` return ${awaitPrefix}self._client.request(`);
10108
10674
  lines.push(` method="${op.httpMethod.toUpperCase()}",`);
10109
10675
  lines.push(` path=${pathExpr},`);
@@ -10335,8 +10901,8 @@ function emitMethodSignature(lines, op, plan, method, isAsync, specEnumNames, mo
10335
10901
  else if (isPaginated) {
10336
10902
  const resolvedItem = resolvePageItemName(op.pagination.itemType, listWrapperNames, ctx);
10337
10903
  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);
10904
+ } else if (isArrayResponse) returnType = `List[${ctx.spec.models.find((m) => m.name === plan.responseModelName)?.discriminator ? className$5(plan.responseModelName) + "Variant" : className$5(plan.responseModelName)}]`;
10905
+ 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
10906
  else returnType = "None";
10341
10907
  lines.push(` ) -> ${returnType}:`);
10342
10908
  return {
@@ -10627,6 +11193,7 @@ function emitMethodBody(lines, op, plan, meta, isAsync, modelImports, listWrappe
10627
11193
  lines.push(" )");
10628
11194
  } else if (plan.hasBody && op.requestBody) {
10629
11195
  const responseModel = plan.responseModelName ? className$5(plan.responseModelName) : "None";
11196
+ const isBodyResponseDiscriminator = !!(plan.responseModelName ? ctx.spec.models.find((m) => m.name === plan.responseModelName) : null)?.discriminator;
10630
11197
  const bodyGroupedParams = collectGroupedParamNames(op);
10631
11198
  const bodyModel = ctx.spec.models.find((m) => op.requestBody?.kind === "model" && m.name === op.requestBody.name);
10632
11199
  const bodyFieldNamesSet = /* @__PURE__ */ new Set();
@@ -10664,6 +11231,20 @@ function emitMethodBody(lines, op, plan, meta, isAsync, modelImports, listWrappe
10664
11231
  lines.push(" request_options=request_options,");
10665
11232
  lines.push(" )");
10666
11233
  lines.push(` return [${itemModel}.from_dict(cast(Dict[str, Any], item)) for item in raw]`);
11234
+ } else if (isBodyResponseDiscriminator && responseModel !== "None") {
11235
+ const variantType = responseModel + "Variant";
11236
+ lines.push(` return cast(`);
11237
+ lines.push(` ${variantType},`);
11238
+ lines.push(` ${awaitPrefix}self._client.request(`);
11239
+ lines.push(` method="${httpMethod}",`);
11240
+ lines.push(` path=${pathStr},`);
11241
+ lines.push(` body=${bodyVarName},`);
11242
+ if (bodyHasParams) lines.push(" params=params,");
11243
+ lines.push(` model=${responseModel}, # type: ignore[arg-type] # dispatcher; request only calls from_dict`);
11244
+ if (plan.isIdempotentPost) lines.push(" idempotency_key=idempotency_key,");
11245
+ lines.push(" request_options=request_options,");
11246
+ lines.push(" )");
11247
+ lines.push(" )");
10667
11248
  } else {
10668
11249
  const bodyReturnPrefix = responseModel !== "None" ? "return " : "";
10669
11250
  lines.push(` ${bodyReturnPrefix}${awaitPrefix}self._client.request(`);
@@ -10678,6 +11259,7 @@ function emitMethodBody(lines, op, plan, meta, isAsync, modelImports, listWrappe
10678
11259
  }
10679
11260
  } else {
10680
11261
  const responseModel = plan.responseModelName ? className$5(plan.responseModelName) : "None";
11262
+ const isGetResponseDiscriminator = !!(plan.responseModelName ? ctx.spec.models.find((m) => m.name === plan.responseModelName) : null)?.discriminator;
10681
11263
  const getGroupedParams = collectGroupedParamNames(op);
10682
11264
  const hasGroups = (op.parameterGroups?.length ?? 0) > 0;
10683
11265
  const visibleQueryParams = op.queryParams.filter((p) => !hiddenParams.has(p.name) && !getGroupedParams.has(p.name));
@@ -10720,6 +11302,18 @@ function emitMethodBody(lines, op, plan, meta, isAsync, modelImports, listWrappe
10720
11302
  lines.push(" request_options=request_options,");
10721
11303
  lines.push(" )");
10722
11304
  lines.push(` return [${itemModel}.from_dict(cast(Dict[str, Any], item)) for item in raw]`);
11305
+ } else if (isGetResponseDiscriminator && responseModel !== "None") {
11306
+ const variantType = responseModel + "Variant";
11307
+ lines.push(` return cast(`);
11308
+ lines.push(` ${variantType},`);
11309
+ lines.push(` ${awaitPrefix}self._client.request(`);
11310
+ lines.push(` method="${httpMethod}",`);
11311
+ lines.push(` path=${pathStr},`);
11312
+ if (emittedParams) lines.push(" params=params,");
11313
+ lines.push(` model=${responseModel}, # type: ignore[arg-type] # dispatcher; request only calls from_dict`);
11314
+ lines.push(" request_options=request_options,");
11315
+ lines.push(" )");
11316
+ lines.push(" )");
10723
11317
  } else {
10724
11318
  const returnPrefix = responseModel !== "None" ? "return " : "";
10725
11319
  lines.push(` ${returnPrefix}${awaitPrefix}self._client.request(`);
@@ -10808,6 +11402,9 @@ function generateResources$6(services, ctx) {
10808
11402
  const plan = planOperation(op);
10809
11403
  if (plan.responseModelName) {
10810
11404
  if (!listWrapperNames.has(plan.responseModelName) || !plan.isPaginated) modelImports.add(plan.responseModelName);
11405
+ if (!plan.isPaginated) {
11406
+ if (ctx.spec.models.find((m) => m.name === plan.responseModelName)?.discriminator) modelImports.add(plan.responseModelName + "Variant");
11407
+ }
10811
11408
  }
10812
11409
  if (op.requestBody?.kind === "model") {
10813
11410
  const requestBodyRef = op.requestBody;
@@ -10867,10 +11464,15 @@ function generateResources$6(services, ctx) {
10867
11464
  }
10868
11465
  }
10869
11466
  const localSet = new Set(localModels);
11467
+ const variantToFile = /* @__PURE__ */ new Map();
11468
+ for (const model of ctx.spec.models) if (model.discriminator) variantToFile.set(model.name + "Variant", fileName$2(model.name));
10870
11469
  if (localModels.length > 0) lines.push(`from .models import ${localModels.map((n) => className$5(n)).join(", ")}`);
10871
11470
  for (const [csDir, names] of [...crossServiceModels].sort()) {
10872
11471
  const unique = names.filter((n) => !localSet.has(n));
10873
- for (const n of unique) lines.push(`from ${ctx.namespace}.${dirToModule(csDir)}.models.${fileName$2(n)} import ${className$5(n)}`);
11472
+ for (const n of unique) {
11473
+ const filePath = variantToFile.get(n) ?? fileName$2(n);
11474
+ lines.push(`from ${ctx.namespace}.${dirToModule(csDir)}.models.${filePath} import ${className$5(n)}`);
11475
+ }
10874
11476
  }
10875
11477
  const enumToServiceMap = placement.enumToService;
10876
11478
  const localEnums = [];
@@ -11101,11 +11703,6 @@ const NON_SPEC_SERVICES = [
11101
11703
  description: "Passwordless (magic-link) session endpoints, not yet in the OpenAPI spec.",
11102
11704
  hasClientAccessor: true
11103
11705
  },
11104
- {
11105
- id: "vault",
11106
- description: "Vault KV storage, key operations, and client-side AES-GCM encrypt/decrypt.",
11107
- hasClientAccessor: true
11108
- },
11109
11706
  {
11110
11707
  id: "webhook_verification",
11111
11708
  description: "Webhook signature verification and event deserialization (H01/H02)."
@@ -11134,14 +11731,6 @@ const PYTHON_NON_SPEC_WIRING = {
11134
11731
  ctorArg: "self",
11135
11732
  docstring: "Passwordless authentication sessions."
11136
11733
  },
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
11734
  actions: {
11146
11735
  importLine: "from .actions import Actions, AsyncActions",
11147
11736
  prop: "actions",
@@ -11469,16 +12058,14 @@ function generateModelFixture$4(model, modelMap, enumMap) {
11469
12058
  });
11470
12059
  for (const field of deduplicatedFields) {
11471
12060
  const wireName = field.name;
11472
- if (field.example !== void 0) fixture[wireName] = field.example;
12061
+ if (field.example !== void 0) fixture[wireName] = normalizeDateTimeExample(field, field.example);
11473
12062
  else fixture[wireName] = generateFieldValue$3(field.type, field.name, model.name, modelMap, enumMap);
11474
12063
  }
11475
12064
  if (model.discriminator) {
11476
12065
  const [firstValue, variantName] = Object.entries(model.discriminator.mapping)[0];
11477
12066
  fixture[model.discriminator.property] = firstValue;
11478
12067
  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
- }
12068
+ 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
12069
  }
11483
12070
  return fixture;
11484
12071
  }
@@ -11505,6 +12092,20 @@ function generateFieldValue$3(ref, fieldName, modelName, modelMap, enumMap) {
11505
12092
  case "map": return { key: generateFieldValue$3(ref.valueType, "value", modelName, modelMap, enumMap) };
11506
12093
  }
11507
12094
  }
12095
+ /**
12096
+ * Normalize date-time example values to millisecond precision so that
12097
+ * round-trip tests (from_dict → to_dict) produce identical output.
12098
+ * Python's _format_datetime always serializes with milliseconds.
12099
+ */
12100
+ function normalizeDateTimeExample(field, value) {
12101
+ if (typeof value !== "string") return value;
12102
+ const ref = field.type.kind === "nullable" ? field.type.inner : field.type;
12103
+ if (ref?.kind !== "primitive" || ref?.type !== "string" || ref?.format !== "date-time") return value;
12104
+ const match = value.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d+)?(Z|[+-]\d{2}:\d{2})$/);
12105
+ if (!match) return value;
12106
+ const [, prefix, , suffix] = match;
12107
+ return `${prefix}.000${suffix}`;
12108
+ }
11508
12109
  function generatePrimitiveValue$3(type, format, name, modelName) {
11509
12110
  switch (type) {
11510
12111
  case "string":
@@ -11609,7 +12210,22 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
11609
12210
  const enumImports = /* @__PURE__ */ new Set();
11610
12211
  for (const op of service.operations) {
11611
12212
  const plan = planOperation(op);
11612
- if (plan.responseModelName) modelImports.add(plan.responseModelName);
12213
+ if (plan.responseModelName) {
12214
+ modelImports.add(plan.responseModelName);
12215
+ if (!plan.isPaginated) {
12216
+ const resolvedVariantClass = resolvePaginatedItemClass(plan.responseModelName, spec);
12217
+ if (resolvedVariantClass && resolvedVariantClass !== className$5(plan.responseModelName)) {
12218
+ const responseModel = spec.models.find((m) => m.name === plan.responseModelName);
12219
+ const disc = responseModel && responseModel.discriminator;
12220
+ if (disc) {
12221
+ for (const variantName of Object.values(disc.mapping)) if (className$5(variantName) === resolvedVariantClass) {
12222
+ modelImports.add(variantName);
12223
+ break;
12224
+ }
12225
+ }
12226
+ }
12227
+ }
12228
+ }
11613
12229
  if (op.pagination?.itemType.kind === "model") {
11614
12230
  modelImports.add(op.pagination.itemType.name);
11615
12231
  let paginationItemName = op.pagination.itemType.name;
@@ -11652,10 +12268,12 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
11652
12268
  if (param.type.kind === "nullable" && param.type.inner.kind === "enum") enumImports.add(param.type.inner.name);
11653
12269
  }
11654
12270
  }
12271
+ const nonPaginatedRefs = collectNonPaginatedResponseModelNames(spec.services);
11655
12272
  const actualImports = [...modelImports].filter((name) => {
11656
12273
  const model = spec.models.find((m) => m.name === name);
11657
12274
  if (!model) return true;
11658
- return !isListWrapperModel(model);
12275
+ if (isListWrapperModel(model) && !nonPaginatedRefs.has(name)) return false;
12276
+ return true;
11659
12277
  });
11660
12278
  const placement = computeSchemaPlacement(spec, ctx);
11661
12279
  const modelToServiceMap = placement.modelToService;
@@ -11794,14 +12412,22 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
11794
12412
  const modelName = plan.responseModelName;
11795
12413
  const fixtureName = `${fileName$2(modelName)}.json`;
11796
12414
  const modelClass = className$5(modelName);
12415
+ const resolvedClass = resolvePaginatedItemClass(modelName, spec) ?? modelClass;
12416
+ const resolvedModelName = resolvedClass !== modelClass ? (() => {
12417
+ const disc = spec.models.find((m) => m.name === modelName)?.discriminator;
12418
+ if (disc) {
12419
+ for (const variantName of Object.values(disc.mapping)) if (className$5(variantName) === resolvedClass) return variantName;
12420
+ }
12421
+ return modelName;
12422
+ })() : modelName;
11797
12423
  lines.push(` def test_${method}(self, workos, httpx_mock):`);
11798
12424
  lines.push(` httpx_mock.add_response(`);
11799
12425
  lines.push(` json=load_fixture("${fixtureName}"),`);
11800
12426
  lines.push(" )");
11801
12427
  const args = buildTestArgs$1(op, spec, hiddenParams);
11802
12428
  lines.push(` result = workos.${propName}.${method}(${args})`);
11803
- lines.push(` assert isinstance(result, ${modelClass})`);
11804
- const assertFields = pickAssertableFields(modelName, spec);
12429
+ lines.push(` assert isinstance(result, ${resolvedClass})`);
12430
+ const assertFields = pickAssertableFields(resolvedModelName, spec);
11805
12431
  for (const af of assertFields) {
11806
12432
  const op_ = af.isBool ? "is" : "==";
11807
12433
  lines.push(` assert result.${af.field} ${op_} ${af.value}`);
@@ -12023,11 +12649,19 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
12023
12649
  const modelName = plan.responseModelName;
12024
12650
  const fixtureName = `${fileName$2(modelName)}.json`;
12025
12651
  const modelClass = className$5(modelName);
12652
+ const asyncResolvedClass = resolvePaginatedItemClass(modelName, spec) ?? modelClass;
12653
+ const asyncResolvedModelName = asyncResolvedClass !== modelClass ? (() => {
12654
+ const disc = spec.models.find((m) => m.name === modelName)?.discriminator;
12655
+ if (disc) {
12656
+ for (const variantName of Object.values(disc.mapping)) if (className$5(variantName) === asyncResolvedClass) return variantName;
12657
+ }
12658
+ return modelName;
12659
+ })() : modelName;
12026
12660
  pushAsyncTestDef(lines, ` async def test_${method}(self, async_workos, httpx_mock):`);
12027
12661
  lines.push(` httpx_mock.add_response(json=load_fixture("${fixtureName}"))`);
12028
12662
  lines.push(` result = await async_workos.${propName}.${method}(${asyncArgs})`);
12029
- lines.push(` assert isinstance(result, ${modelClass})`);
12030
- const assertFields = pickAssertableFields(modelName, spec);
12663
+ lines.push(` assert isinstance(result, ${asyncResolvedClass})`);
12664
+ const assertFields = pickAssertableFields(asyncResolvedModelName, spec);
12031
12665
  for (const af of assertFields) {
12032
12666
  const op_ = af.isBool ? "is" : "==";
12033
12667
  lines.push(` assert result.${af.field} ${op_} ${af.value}`);
@@ -12154,7 +12788,7 @@ function emitWrapperTests$1(lines, resolvedOps, propName, spec, ctx, isAsync) {
12154
12788
  for (const wrapper of r.wrappers) {
12155
12789
  const method = wrapper.name;
12156
12790
  const wrapperParams = resolveWrapperParams(wrapper, ctx);
12157
- const responseType = wrapper.responseModelName ? className$5(wrapper.responseModelName) : null;
12791
+ const resolvedResponseClass = wrapper.responseModelName ? resolvePaginatedItemClass(wrapper.responseModelName, spec) ?? className$5(wrapper.responseModelName) : null;
12158
12792
  const fixtureName = wrapper.responseModelName ? `${fileName$2(wrapper.responseModelName)}.json` : null;
12159
12793
  const argParts = [];
12160
12794
  for (const { paramName, field, isOptional } of wrapperParams) {
@@ -12170,7 +12804,7 @@ function emitWrapperTests$1(lines, resolvedOps, propName, spec, ctx, isAsync) {
12170
12804
  if (fixtureName) {
12171
12805
  lines.push(` httpx_mock.add_response(json=load_fixture("${fixtureName}"))`);
12172
12806
  lines.push(` result = await async_workos.${propName}.${method}(${args})`);
12173
- if (responseType) lines.push(` assert isinstance(result, ${responseType})`);
12807
+ if (resolvedResponseClass) lines.push(` assert isinstance(result, ${resolvedResponseClass})`);
12174
12808
  } else {
12175
12809
  lines.push(" httpx_mock.add_response(json={})");
12176
12810
  lines.push(` await async_workos.${propName}.${method}(${args})`);
@@ -12180,7 +12814,7 @@ function emitWrapperTests$1(lines, resolvedOps, propName, spec, ctx, isAsync) {
12180
12814
  if (fixtureName) {
12181
12815
  lines.push(` httpx_mock.add_response(json=load_fixture("${fixtureName}"))`);
12182
12816
  lines.push(` result = workos.${propName}.${method}(${args})`);
12183
- if (responseType) lines.push(` assert isinstance(result, ${responseType})`);
12817
+ if (resolvedResponseClass) lines.push(` assert isinstance(result, ${resolvedResponseClass})`);
12184
12818
  } else {
12185
12819
  lines.push(" httpx_mock.add_response(json={})");
12186
12820
  lines.push(` workos.${propName}.${method}(${args})`);
@@ -12950,8 +13584,9 @@ function generateModels$5(models, ctx) {
12950
13584
  overwriteExisting: true
12951
13585
  });
12952
13586
  const nonPaginatedRefs = collectNonPaginatedResponseModelNames(ctx.spec.services);
13587
+ const listMetadataNeeded = collectReferencedListMetadataModels(models, nonPaginatedRefs);
12953
13588
  for (const model of models) {
12954
- if (isListMetadataModel(model)) continue;
13589
+ if (isListMetadataModel(model) && !listMetadataNeeded.has(model.name)) continue;
12955
13590
  if (isListWrapperModel(model) && !nonPaginatedRefs.has(model.name)) continue;
12956
13591
  const name = className$4(model.name);
12957
13592
  const lines = [];
@@ -13484,7 +14119,8 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
13484
14119
  const method = resolveMethodName$4(op, service, ctx);
13485
14120
  const hiddenParams = new Set([...Object.keys(getOpDefaults(resolvedOp)), ...getOpInferFromClient(resolvedOp)]);
13486
14121
  const isRedirect = isRedirectEndpoint(op, resolvedOp);
13487
- const params = buildMethodParams(op, plan, modelMap, ctx, hiddenParams);
14122
+ const materializeQueryDefaults = !isRedirect;
14123
+ const params = buildMethodParams(op, plan, modelMap, ctx, hiddenParams, { materializeQueryDefaults });
13488
14124
  const returnType = isRedirect ? "string" : getReturnType(plan, ctx);
13489
14125
  const docParts = [];
13490
14126
  if (op.description) docParts.push(op.description);
@@ -13532,7 +14168,7 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
13532
14168
  const phpName = fieldName$4(q.name);
13533
14169
  if (seenDocParams.has(phpName)) continue;
13534
14170
  seenDocParams.add(phpName);
13535
- const hasEnumDefault = q.default != null && q.type.kind === "enum";
14171
+ const hasEnumDefault = shouldMaterializeQueryDefault(q, materializeQueryDefaults);
13536
14172
  const nullSuffix = !q.required && !hasEnumDefault && !docType.endsWith("|null") ? "|null" : "";
13537
14173
  const prefix = q.deprecated ? "(deprecated) " : "";
13538
14174
  let desc = q.description ? ` ${prefix}${q.description}` : q.deprecated ? " (deprecated)" : "";
@@ -13563,7 +14199,7 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
13563
14199
  const httpMethod = op.httpMethod.toUpperCase();
13564
14200
  const path = buildPathString(op);
13565
14201
  if (isRedirect) {
13566
- const queryLines = buildQueryArray(op, hiddenParams);
14202
+ const queryLines = buildQueryArray(op, hiddenParams, { materializeQueryDefaults });
13567
14203
  const hasDefaults = Object.keys(getOpDefaults(resolvedOp)).length > 0;
13568
14204
  const hasInferred = getOpInferFromClient(resolvedOp).length > 0;
13569
14205
  const hasGroups = (op.parameterGroups?.length ?? 0) > 0;
@@ -13707,7 +14343,7 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
13707
14343
  }
13708
14344
  lines.push(" }");
13709
14345
  }
13710
- function buildMethodParams(op, plan, modelMap, ctx, hiddenParams) {
14346
+ function buildMethodParams(op, plan, modelMap, ctx, hiddenParams, opts) {
13711
14347
  const required = [];
13712
14348
  const optional = [];
13713
14349
  const usedNames = /* @__PURE__ */ new Set();
@@ -13755,7 +14391,7 @@ function buildMethodParams(op, plan, modelMap, ctx, hiddenParams) {
13755
14391
  if (usedNames.has(phpName)) continue;
13756
14392
  usedNames.add(phpName);
13757
14393
  if (q.required) required.push(`${phpType} $${phpName}`);
13758
- else if (q.default != null && q.type.kind === "enum") {
14394
+ else if (shouldMaterializeQueryDefault(q, opts?.materializeQueryDefaults ?? true)) {
13759
14395
  const enumType = mapTypeRef$5(q.type, { qualified: true });
13760
14396
  const caseName = toPascalCase(String(q.default));
13761
14397
  optional.push(`${enumType} $${phpName} = ${enumType}::${caseName}`);
@@ -13801,19 +14437,31 @@ function isEnumType(ref) {
13801
14437
  if (ref.kind === "nullable") return isEnumType(ref.inner);
13802
14438
  return false;
13803
14439
  }
13804
- function buildQueryArray(op, hiddenParams) {
14440
+ function isDateTimeType(ref) {
14441
+ if (ref.kind === "primitive" && ref.format === "date-time") return true;
14442
+ if (ref.kind === "nullable") return isDateTimeType(ref.inner);
14443
+ return false;
14444
+ }
14445
+ function buildQueryArray(op, hiddenParams, opts) {
13805
14446
  const hidden = hiddenParams ?? /* @__PURE__ */ new Set();
13806
14447
  const groupedParams = collectGroupedParamNames(op);
13807
14448
  return op.queryParams.filter((q) => !hidden.has(q.name) && !groupedParams.has(q.name)).map((q) => {
13808
14449
  const phpName = fieldName$4(q.name);
13809
14450
  if (isEnumType(q.type)) {
13810
- const hasEnumDefault = q.default != null && q.type.kind === "enum";
14451
+ const hasEnumDefault = shouldMaterializeQueryDefault(q, opts?.materializeQueryDefaults ?? true);
13811
14452
  const nullsafe = q.required || hasEnumDefault ? "" : "?";
13812
14453
  return `'${q.name}' => $${phpName}${nullsafe}->value,`;
13813
14454
  }
14455
+ if (isDateTimeType(q.type)) {
14456
+ const nullsafe = q.required ? "" : "?";
14457
+ return `'${q.name}' => $${phpName}${nullsafe}->format(\\DateTimeInterface::RFC3339_EXTENDED),`;
14458
+ }
13814
14459
  return `'${q.name}' => $${phpName},`;
13815
14460
  });
13816
14461
  }
14462
+ function shouldMaterializeQueryDefault(param, enabled) {
14463
+ return enabled && param.default != null && param.type.kind === "enum";
14464
+ }
13817
14465
  function phpLiteral(value) {
13818
14466
  if (typeof value === "string") return `'${value}'`;
13819
14467
  if (typeof value === "number") return String(value);
@@ -14005,8 +14653,10 @@ function generateFixtures$3(spec) {
14005
14653
  const modelMap = new Map(spec.models.map((m) => [m.name, m]));
14006
14654
  const enumMap = new Map(spec.enums.map((e) => [e.name, e]));
14007
14655
  const files = [];
14656
+ const nonPaginatedRefs = collectNonPaginatedResponseModelNames(spec.services);
14657
+ const listMetadataNeeded = collectReferencedListMetadataModels(spec.models, nonPaginatedRefs);
14008
14658
  for (const model of spec.models) {
14009
- if (isListMetadataModel(model)) continue;
14659
+ if (isListMetadataModel(model) && !listMetadataNeeded.has(model.name)) continue;
14010
14660
  if (isListWrapperModel(model)) continue;
14011
14661
  const fixture = generateModelFixture$3(model, modelMap, enumMap);
14012
14662
  files.push({
@@ -14351,13 +15001,15 @@ function buildTestArgs(op, ctx, opts) {
14351
15001
  }
14352
15002
  function generateTestValue(ref, ctx) {
14353
15003
  switch (ref.kind) {
14354
- case "primitive": switch (ref.type) {
14355
- case "string": return "'test_value'";
14356
- case "integer": return "1";
14357
- case "number": return "1.0";
14358
- case "boolean": return "true";
14359
- default: return "'test_value'";
14360
- }
15004
+ case "primitive":
15005
+ if (ref.format === "date-time") return "new \\DateTimeImmutable('2023-01-01T00:00:00Z')";
15006
+ switch (ref.type) {
15007
+ case "string": return "'test_value'";
15008
+ case "integer": return "1";
15009
+ case "number": return "1.0";
15010
+ case "boolean": return "true";
15011
+ default: return "'test_value'";
15012
+ }
14361
15013
  case "enum":
14362
15014
  if (ctx && ref.name) {
14363
15015
  const e = ctx.spec.enums.find((en) => en.name === ref.name);
@@ -14444,7 +15096,8 @@ function emitQueryAssertions(lines, op, ctx, hidden) {
14444
15096
  if (innerType.kind === "enum" && innerType.name) {
14445
15097
  const e = ctx.spec.enums.find((en) => en.name === innerType.name);
14446
15098
  if (e && e.values.length > 0) lines.push(` $this->assertSame('${e.values[0].value}', $query['${q.name}']);`);
14447
- } else if (innerType.kind === "primitive") switch (innerType.type) {
15099
+ } else if (innerType.kind === "primitive") if (innerType.format === "date-time") lines.push(` $this->assertArrayHasKey('${q.name}', $query);`);
15100
+ else switch (innerType.type) {
14448
15101
  case "string":
14449
15102
  lines.push(` $this->assertSame('test_value', $query['${q.name}']);`);
14450
15103
  break;
@@ -15154,6 +15807,8 @@ function generateEnums$4(enums, ctx) {
15154
15807
  content: lines.join("\n"),
15155
15808
  overwriteExisting: true
15156
15809
  });
15810
+ const eventConstantsFile = generateEventConstantsFile(enums);
15811
+ if (eventConstantsFile) files.push(eventConstantsFile);
15157
15812
  return files;
15158
15813
  }
15159
15814
  /** Known acronyms to preserve as single tokens during humanization. */
@@ -15214,6 +15869,56 @@ function collectEnumAliasOf$2(enums) {
15214
15869
  }
15215
15870
  return aliasOf;
15216
15871
  }
15872
+ function generateEventConstantsFile(enums) {
15873
+ const enumDef = findWebhookEventEnum(enums);
15874
+ if (!enumDef) return null;
15875
+ const lines = [];
15876
+ lines.push("package events");
15877
+ lines.push("");
15878
+ lines.push("// Event is a WorkOS event type.");
15879
+ lines.push("type Event string");
15880
+ lines.push("");
15881
+ lines.push("const (");
15882
+ const seenValues = /* @__PURE__ */ new Set();
15883
+ const usedNames = /* @__PURE__ */ new Set();
15884
+ for (const value of enumDef.values) {
15885
+ const valueStr = String(value.value);
15886
+ if (seenValues.has(valueStr)) continue;
15887
+ seenValues.add(valueStr);
15888
+ const constName = uniqueEventConstantName(valueStr, usedNames);
15889
+ usedNames.add(constName);
15890
+ if (value.description) lines.push(`\t// ${constName} is ${value.description}.`);
15891
+ if (value.deprecated) {
15892
+ if (value.description) lines.push(" //");
15893
+ lines.push(" // Deprecated: this value is deprecated.");
15894
+ }
15895
+ lines.push(`\t${constName} = "${escapeGoString$1(valueStr)}"`);
15896
+ }
15897
+ lines.push(")");
15898
+ lines.push("");
15899
+ return {
15900
+ path: "pkg/events/events.go",
15901
+ content: lines.join("\n"),
15902
+ overwriteExisting: true
15903
+ };
15904
+ }
15905
+ function findWebhookEventEnum(enums) {
15906
+ 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;
15907
+ }
15908
+ function isWebhookEventEnumName(name) {
15909
+ const normalized = name.toLowerCase();
15910
+ return normalized.includes("webhook") && normalized.includes("event");
15911
+ }
15912
+ function uniqueEventConstantName(value, usedNames) {
15913
+ const base = className$3(value);
15914
+ if (!usedNames.has(base)) return base;
15915
+ let suffix = 2;
15916
+ while (usedNames.has(`${base}${suffix}`)) suffix++;
15917
+ return `${base}${suffix}`;
15918
+ }
15919
+ function escapeGoString$1(value) {
15920
+ return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
15921
+ }
15217
15922
  //#endregion
15218
15923
  //#region src/go/wrappers.ts
15219
15924
  /**
@@ -16262,7 +16967,7 @@ function generateClient$4(spec, ctx) {
16262
16967
  return [generateWorkOSFile(spec, ctx)];
16263
16968
  }
16264
16969
  /**
16265
- * Non-spec services marked with `hasClientAccessor: true` (passwordless, vault)
16970
+ * Non-spec services marked with `hasClientAccessor: true` (e.g. passwordless)
16266
16971
  * are included in the generated Client struct, constructor, and accessor methods
16267
16972
  * — identical to spec-driven services. Their service type (e.g. PasswordlessService)
16268
16973
  * is defined in a hand-written @oagen-ignore-file, but the Client wiring is generated.
@@ -20031,12 +20736,19 @@ function generateEnums$2(enums, _ctx) {
20031
20736
  hashGroups.get(hash).push(enumDef);
20032
20737
  }
20033
20738
  const aliasOf = /* @__PURE__ */ new Map();
20739
+ const sharedSortEmitters = /* @__PURE__ */ new Set();
20034
20740
  for (const [, group] of hashGroups) {
20035
- if (group.length <= 1) continue;
20741
+ if (group.length <= 1) {
20742
+ if (group.length === 1 && isSharedSortOrderEnum(group[0])) {
20743
+ enumCanonicalMap.set(group[0].name, "SortOrder");
20744
+ sharedSortEmitters.add(group[0].name);
20745
+ }
20746
+ continue;
20747
+ }
20036
20748
  if (group.every(isSharedSortOrderEnum)) {
20037
20749
  const [canonical, ...rest] = [...group].sort((a, b) => a.name.localeCompare(b.name));
20038
- enumCanonicalMap.set(canonical.name, canonical.name);
20039
- for (const enumDef of rest) enumCanonicalMap.set(enumDef.name, "SortOrder");
20750
+ sharedSortEmitters.add(canonical.name);
20751
+ for (const enumDef of [canonical, ...rest]) enumCanonicalMap.set(enumDef.name, "SortOrder");
20040
20752
  continue;
20041
20753
  }
20042
20754
  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 +20762,7 @@ function generateEnums$2(enums, _ctx) {
20050
20762
  for (const enumDef of enums) {
20051
20763
  if (enumDef.values.length === 0) continue;
20052
20764
  const typeName = canonicalEnumTypeName(enumDef);
20053
- const canonicalName = isSharedSortOrderEnum(enumDef) && enumCanonicalMap.get(enumDef.name) === enumDef.name ? void 0 : aliasOf.get(enumDef.name) ?? enumCanonicalMap.get(enumDef.name);
20765
+ const canonicalName = sharedSortEmitters.has(enumDef.name) ? void 0 : aliasOf.get(enumDef.name) ?? enumCanonicalMap.get(enumDef.name);
20054
20766
  if (canonicalName) {
20055
20767
  const canonicalType = className$1(canonicalName);
20056
20768
  if (typeName === canonicalType) continue;
@@ -24298,11 +25010,6 @@ const NON_SPEC_ACCESSORS = {
24298
25010
  className: "Passwordless",
24299
25011
  ctorArg: "self"
24300
25012
  },
24301
- vault: {
24302
- prop: "vault",
24303
- className: "Vault",
24304
- ctorArg: "self"
24305
- },
24306
25013
  actions: {
24307
25014
  prop: "actions",
24308
25015
  className: "Actions",
@@ -26119,6 +26826,7 @@ function classifyGroup(group, op) {
26119
26826
  function renderParamsStruct(name, op, resolved, registry, ctx, groupEmitter) {
26120
26827
  const bodyRequired = isBodyRequired(op);
26121
26828
  const hidden = new Set([...Object.keys(resolved.defaults ?? {}), ...resolved.inferFromClient ?? []]);
26829
+ const materializeSpecDefaults = !resolved.urlBuilder;
26122
26830
  const queryGroupParamNames = /* @__PURE__ */ new Set();
26123
26831
  const bodyGroupParamNames = /* @__PURE__ */ new Set();
26124
26832
  const queryGroupFields = [];
@@ -26155,7 +26863,7 @@ function renderParamsStruct(name, op, resolved, registry, ctx, groupEmitter) {
26155
26863
  });
26156
26864
  if (!p.required && !rust.startsWith("Option<")) rust = makeOptional(rust);
26157
26865
  rust = applySecretRedaction(rust, p.name);
26158
- const defaultExpr = p.default != null ? rustDefaultExpr(p.default, p.type, rust.startsWith("Option<"), ctx) : null;
26866
+ const defaultExpr = materializeSpecDefaults && p.default != null ? rustDefaultExpr(p.default, p.type, rust.startsWith("Option<"), ctx) : null;
26159
26867
  const desc = p.description?.trim();
26160
26868
  if (desc) for (const c of paramDocComment(desc)) fieldLines.push(` ${c}`);
26161
26869
  if (p.default != null) {
@@ -26788,7 +27496,7 @@ function renderResourcesBarrel(exports) {
26788
27496
  }
26789
27497
  unique.sort((a, b) => a.module.localeCompare(b.module));
26790
27498
  const lines = [];
26791
- for (const { module } of unique) lines.push(`mod ${module};`);
27499
+ for (const { module } of unique) lines.push(`pub mod ${module};`);
26792
27500
  lines.push("");
26793
27501
  for (const { module, struct } of unique) lines.push(`pub use ${module}::${struct};`);
26794
27502
  return lines.join("\n") + "\n";
@@ -27650,4 +28358,4 @@ const workosEmittersPlugin = {
27650
28358
  //#endregion
27651
28359
  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
28360
 
27653
- //# sourceMappingURL=plugin-BGVaMGqe.mjs.map
28361
+ //# sourceMappingURL=plugin-CO4RFgAW.mjs.map