@workos/oagen-emitters 0.6.7 → 0.7.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.
@@ -3016,7 +3016,7 @@ function buildKnownTypeNames(models, ctx) {
3016
3016
  * the output directory for a given IR service name.
3017
3017
  */
3018
3018
  function createServiceDirResolver(models, services, ctx) {
3019
- const modelToService = assignModelsToServices(models, services);
3019
+ const modelToService = assignModelsToServices(models, services, ctx.modelHints);
3020
3020
  const serviceNameMap = buildServiceNameMap(services, ctx);
3021
3021
  const resolveDir = (irService) => irService ? resolveServiceDir$1(serviceNameMap.get(irService) ?? irService) : "common";
3022
3022
  return {
@@ -6532,7 +6532,7 @@ function modelNeedsRoundTripTest(model) {
6532
6532
  */
6533
6533
  function generateSerializerTests(spec, ctx) {
6534
6534
  const files = [];
6535
- const modelToService = assignModelsToServices$1(spec.models, spec.services);
6535
+ const modelToService = assignModelsToServices$1(spec.models, spec.services, ctx.modelHints);
6536
6536
  const serviceNameMap = /* @__PURE__ */ new Map();
6537
6537
  for (const service of spec.services) serviceNameMap.set(service.name, resolveResourceClassName$3(service, ctx));
6538
6538
  const resolveDir = (irService) => irService ? resolveServiceDir$1(serviceNameMap.get(irService) ?? irService) : "common";
@@ -7100,7 +7100,7 @@ function assignEnumsToServices(enums, services) {
7100
7100
  */
7101
7101
  function generateModels$5(models, ctx) {
7102
7102
  if (models.length === 0) return [];
7103
- const modelToService = assignModelsToServices(models, ctx.spec.services);
7103
+ const modelToService = assignModelsToServices(models, ctx.spec.services, ctx.modelHints);
7104
7104
  const enumToService = assignEnumsToServices(ctx.spec.enums, ctx.spec.services);
7105
7105
  const mountDirMap = buildMountDirMap$1(ctx);
7106
7106
  const resolveDir = (irService) => irService ? mountDirMap.get(irService) ?? "common" : "common";
@@ -7782,7 +7782,7 @@ const buildHiddenParams = buildHiddenParams$1;
7782
7782
  * PascalCase variant class name for a parameter group variant.
7783
7783
  * E.g., group "parent_resource", variant "by_id" -> "ParentResourceById".
7784
7784
  */
7785
- function groupVariantClassName$2(groupName, variantName) {
7785
+ function groupVariantClassName$3(groupName, variantName) {
7786
7786
  return className$5(`${groupName}_${variantName}`);
7787
7787
  }
7788
7788
  /**
@@ -7796,7 +7796,7 @@ function generateParameterGroupDataclasses(operations, specEnumNames, models) {
7796
7796
  for (const op of operations) {
7797
7797
  const bodyFieldTypes = collectBodyFieldTypes(op, models);
7798
7798
  for (const group of op.parameterGroups ?? []) for (const variant of group.variants) {
7799
- const variantClass = groupVariantClassName$2(group.name, variant.name);
7799
+ const variantClass = groupVariantClassName$3(group.name, variant.name);
7800
7800
  if (emitted.has(variantClass)) continue;
7801
7801
  emitted.add(variantClass);
7802
7802
  lines.push("");
@@ -7829,7 +7829,7 @@ function collectParameterGroupClassNames(operations) {
7829
7829
  const names = [];
7830
7830
  const seen = /* @__PURE__ */ new Set();
7831
7831
  for (const op of operations) for (const group of op.parameterGroups ?? []) for (const variant of group.variants) {
7832
- const cls = groupVariantClassName$2(group.name, variant.name);
7832
+ const cls = groupVariantClassName$3(group.name, variant.name);
7833
7833
  if (!seen.has(cls)) {
7834
7834
  seen.add(cls);
7835
7835
  names.push(cls);
@@ -7924,7 +7924,7 @@ function emitMethodSignature(lines, op, plan, method, isAsync, specEnumNames, mo
7924
7924
  }
7925
7925
  }
7926
7926
  for (const group of op.parameterGroups ?? []) {
7927
- const unionType = `Union[${group.variants.map((v) => groupVariantClassName$2(group.name, v.name)).join(", ")}]`;
7927
+ const unionType = `Union[${group.variants.map((v) => groupVariantClassName$3(group.name, v.name)).join(", ")}]`;
7928
7928
  const paramName = fieldName$4(group.name);
7929
7929
  if (group.optional) lines.push(` ${paramName}: Optional[${unionType}] = None,`);
7930
7930
  else lines.push(` ${paramName}: ${unionType},`);
@@ -8050,7 +8050,7 @@ function emitMethodDocstring(lines, op, plan, method, meta, specEnumNames, ctx,
8050
8050
  }
8051
8051
  }
8052
8052
  for (const group of op.parameterGroups ?? []) {
8053
- const variantClasses = group.variants.map((v) => groupVariantClassName$2(group.name, v.name));
8053
+ const variantClasses = group.variants.map((v) => groupVariantClassName$3(group.name, v.name));
8054
8054
  const desc = `Identifies the ${group.name.replace(/_/g, " ")}. One of: ${variantClasses.join(", ")}.`;
8055
8055
  allParams.push({
8056
8056
  name: fieldName$4(group.name),
@@ -8357,7 +8357,7 @@ function emitGroupDispatch(lines, op, target = "params", bodyFieldTypes) {
8357
8357
  const indent = group.optional ? " " : "";
8358
8358
  let first = true;
8359
8359
  for (const variant of group.variants) {
8360
- const variantClass = groupVariantClassName$2(group.name, variant.name);
8360
+ const variantClass = groupVariantClassName$3(group.name, variant.name);
8361
8361
  const keyword = first ? "if" : "elif";
8362
8362
  first = false;
8363
8363
  lines.push(` ${indent}${keyword} isinstance(${groupParam}, ${variantClass}):`);
@@ -8453,7 +8453,7 @@ function generateResources$5(services, ctx) {
8453
8453
  if (enumImports.size > 0) lines.push(`from ${importPrefix}_types import RequestOptions, enum_value`);
8454
8454
  else lines.push(`from ${importPrefix}_types import RequestOptions`);
8455
8455
  const actualModelImports = [...modelImports];
8456
- const modelToServiceMap = assignModelsToServices(ctx.spec.models, ctx.spec.services);
8456
+ const modelToServiceMap = assignModelsToServices(ctx.spec.models, ctx.spec.services, ctx.modelHints);
8457
8457
  for (const model of ctx.spec.models) if (model.discriminator) {
8458
8458
  const svc = modelToServiceMap.get(model.name);
8459
8459
  if (svc) modelToServiceMap.set(model.name + "Variant", svc);
@@ -9270,7 +9270,7 @@ function generateServiceTest$2(service, spec, ctx, accessPaths, resolvedOps) {
9270
9270
  if (!model) return true;
9271
9271
  return !isListWrapperModel(model);
9272
9272
  });
9273
- const modelToServiceMap = assignModelsToServices(spec.models, spec.services);
9273
+ const modelToServiceMap = assignModelsToServices(spec.models, spec.services, ctx.modelHints);
9274
9274
  const mountDirMap = buildMountDirMap$1(ctx);
9275
9275
  const resolveModelDir = (modelName) => {
9276
9276
  const svc = modelToServiceMap.get(modelName);
@@ -10120,7 +10120,7 @@ function generateModelRoundTripTests(spec, ctx) {
10120
10120
  for (const name of responseModelNames) requestOnlyModelNames.delete(name);
10121
10121
  const models = spec.models.filter((m) => !isListWrapperModel(m) && !isListMetadataModel(m) && !requestOnlyModelNames.has(m.name));
10122
10122
  if (models.length === 0) return null;
10123
- const modelToService = assignModelsToServices(spec.models, spec.services);
10123
+ const modelToService = assignModelsToServices(spec.models, spec.services, ctx.modelHints);
10124
10124
  const roundTripDirMap = buildMountDirMap$1(ctx);
10125
10125
  const resolveDir = (irService) => irService ? roundTripDirMap.get(irService) ?? "common" : "common";
10126
10126
  const lines = [];
@@ -10928,7 +10928,7 @@ function isRedirectEndpoint(op, resolvedOp) {
10928
10928
  return false;
10929
10929
  }
10930
10930
  /** PHP class name for a parameter group variant (e.g. ParentResourceById). */
10931
- function groupVariantClassName$1(groupName, variantName) {
10931
+ function groupVariantClassName$2(groupName, variantName) {
10932
10932
  return `${className$4(groupName)}${className$4(variantName)}`;
10933
10933
  }
10934
10934
  /**
@@ -10948,7 +10948,7 @@ function generateParameterGroupFiles(op, ctx, modelMap) {
10948
10948
  const files = [];
10949
10949
  const bodyFieldTypes = collectBodyFieldTypes(op, [...modelMap.values()]);
10950
10950
  for (const group of op.parameterGroups ?? []) for (const variant of group.variants) {
10951
- const variantClass = groupVariantClassName$1(group.name, variant.name);
10951
+ const variantClass = groupVariantClassName$2(group.name, variant.name);
10952
10952
  const lines = [];
10953
10953
  lines.push(`namespace ${ctx.namespacePascal}\\Service;`);
10954
10954
  lines.push("");
@@ -10982,7 +10982,7 @@ function generateGroupDispatch(op, indent, target = "$query") {
10982
10982
  const phpParamName = fieldName$3(group.name);
10983
10983
  for (let vi = 0; vi < group.variants.length; vi++) {
10984
10984
  const variant = group.variants[vi];
10985
- const variantClass = groupVariantClassName$1(group.name, variant.name);
10985
+ const variantClass = groupVariantClassName$2(group.name, variant.name);
10986
10986
  const keyword = vi === 0 ? "if" : "elseif";
10987
10987
  lines.push(`${indent}${keyword} ($${phpParamName} instanceof ${variantClass}) {`);
10988
10988
  for (const param of variant.parameters) {
@@ -11036,7 +11036,7 @@ function generateMethod$2(lines, op, service, ctx, modelMap, resolvedOp) {
11036
11036
  const phpName = fieldName$3(group.name);
11037
11037
  if (seenDocParams.has(phpName)) continue;
11038
11038
  seenDocParams.add(phpName);
11039
- const unionDocType = group.variants.map((v) => groupVariantClassName$1(group.name, v.name)).join("|");
11039
+ const unionDocType = group.variants.map((v) => groupVariantClassName$2(group.name, v.name)).join("|");
11040
11040
  const nullPrefix = group.optional ? "null|" : "";
11041
11041
  docParts.push(`@param ${nullPrefix}${unionDocType} $${phpName}`);
11042
11042
  }
@@ -11258,7 +11258,7 @@ function buildMethodParams(op, plan, modelMap, ctx, hiddenParams) {
11258
11258
  const phpName = fieldName$3(group.name);
11259
11259
  if (usedNames.has(phpName)) continue;
11260
11260
  usedNames.add(phpName);
11261
- const unionType = group.variants.map((v) => groupVariantClassName$1(group.name, v.name)).join("|");
11261
+ const unionType = group.variants.map((v) => groupVariantClassName$2(group.name, v.name)).join("|");
11262
11262
  if (group.optional) optional.push(`null|${unionType} $${phpName} = null`);
11263
11263
  else required.push(`${unionType} $${phpName}`);
11264
11264
  }
@@ -15565,7 +15565,7 @@ function groupBaseClassName(mountName, groupName) {
15565
15565
  return `${className$2(mountName)}${className$2(groupName)}`;
15566
15566
  }
15567
15567
  /** Concrete variant class name (e.g. UserManagementRoleSingle). */
15568
- function groupVariantClassName(mountName, groupName, variantName) {
15568
+ function groupVariantClassName$1(mountName, groupName, variantName) {
15569
15569
  return `${className$2(mountName)}${className$2(groupName)}${className$2(variantName)}`;
15570
15570
  }
15571
15571
  /**
@@ -15583,7 +15583,7 @@ function generateParameterGroupTypes(mountName, op, models, emitted) {
15583
15583
  lines.push("");
15584
15584
  lines.push(` public abstract class ${baseName} { }`);
15585
15585
  for (const variant of group.variants) {
15586
- const variantName = groupVariantClassName(mountName, group.name, variant.name);
15586
+ const variantName = groupVariantClassName$1(mountName, group.name, variant.name);
15587
15587
  lines.push("");
15588
15588
  lines.push(` public class ${variantName} : ${baseName}`);
15589
15589
  lines.push(" {");
@@ -15611,7 +15611,7 @@ function emitGroupSerialization(mountName, op, indent, models, target) {
15611
15611
  const groupField = fieldName$1(group.name);
15612
15612
  let first = true;
15613
15613
  for (const variant of group.variants) {
15614
- const variantName = groupVariantClassName(mountName, group.name, variant.name);
15614
+ const variantName = groupVariantClassName$1(mountName, group.name, variant.name);
15615
15615
  const localVar = localName(variant.name);
15616
15616
  const keyword = first ? "if" : "else if";
15617
15617
  first = false;
@@ -19677,6 +19677,22 @@ function safeParamName(name) {
19677
19677
  function moduleName(name) {
19678
19678
  return toSnakeCase(name);
19679
19679
  }
19680
+ /**
19681
+ * PascalCase class name for a parameter-group variant. Mirrors the Python
19682
+ * convention: group "password" + variant "plaintext" → `PasswordPlaintext`.
19683
+ * Used as the Ruby constant under the WorkOS module.
19684
+ */
19685
+ function groupVariantClassName(groupName, variantName) {
19686
+ return className(`${groupName}_${variantName}`);
19687
+ }
19688
+ /**
19689
+ * Fully-qualified Ruby constant for a parameter-group variant scoped under
19690
+ * its owning resource module — e.g. "WorkOS::UserManagement::PasswordPlaintext".
19691
+ * Mirrors Python's `workos.user_management.PasswordPlaintext` namespacing.
19692
+ */
19693
+ function scopedGroupVariantClassName(mountTarget, groupName, variantName) {
19694
+ return `WorkOS::${className(mountTarget)}::${groupVariantClassName(groupName, variantName)}`;
19695
+ }
19680
19696
  /** snake_case property name for service accessors on the client. */
19681
19697
  function servicePropertyName(name) {
19682
19698
  return toSnakeCase(name);
@@ -19762,7 +19778,7 @@ function generateModels(models, ctx) {
19762
19778
  if (models.length === 0) return [];
19763
19779
  const enumNames = new Set(ctx.spec.enums.map((e) => e.name));
19764
19780
  const modelNames = new Set(models.map((m) => m.name));
19765
- const modelToService = assignModelsToServices(models, ctx.spec.services);
19781
+ const modelToService = assignModelsToServices(models, ctx.spec.services, ctx.modelHints);
19766
19782
  const mountDirMap = buildMountDirMap(ctx);
19767
19783
  const dirFor = (modelName) => {
19768
19784
  const service = modelToService.get(modelName);
@@ -20233,6 +20249,185 @@ function rubyStringLit$1(s) {
20233
20249
  return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
20234
20250
  }
20235
20251
  //#endregion
20252
+ //#region src/ruby/parameter-groups.ts
20253
+ /**
20254
+ * Sorbet type string for a TypeRef. Mirrors `mapSorbetType` in rbi.ts but
20255
+ * lives here so the parameter-groups module is self-contained.
20256
+ */
20257
+ function mapSorbetType$1(ref) {
20258
+ switch (ref.kind) {
20259
+ case "primitive":
20260
+ switch (ref.type) {
20261
+ case "string": return "String";
20262
+ case "integer": return "Integer";
20263
+ case "number": return "Float";
20264
+ case "boolean": return "T::Boolean";
20265
+ case "unknown": return "T.untyped";
20266
+ }
20267
+ break;
20268
+ case "array": return `T::Array[${mapSorbetType$1(ref.items)}]`;
20269
+ case "model": return `WorkOS::${className(ref.name)}`;
20270
+ case "enum": return "String";
20271
+ case "nullable": return `T.nilable(${mapSorbetType$1(ref.inner)})`;
20272
+ case "literal":
20273
+ if (typeof ref.value === "string") return "String";
20274
+ if (ref.value === null) return "NilClass";
20275
+ if (typeof ref.value === "number") return Number.isInteger(ref.value) ? "Integer" : "Float";
20276
+ return "T::Boolean";
20277
+ case "union": {
20278
+ const variants = ref.variants.map((v) => mapSorbetType$1(v));
20279
+ const unique = [...new Set(variants)];
20280
+ if (unique.length === 1) return unique[0];
20281
+ return `T.any(${unique.join(", ")})`;
20282
+ }
20283
+ case "map": return `T::Hash[String, ${mapSorbetType$1(ref.valueType)}]`;
20284
+ }
20285
+ return "T.untyped";
20286
+ }
20287
+ /**
20288
+ * Build a stable groupName -> mountTarget map. Each parameter group is owned
20289
+ * by exactly one resource module — Ruby variant classes are inlined into
20290
+ * `WorkOS::<MountTarget>::<Variant>` (matching Python's per-resource layout),
20291
+ * so a dispatcher in another resource that references the same group still
20292
+ * resolves to a single canonical class.
20293
+ *
20294
+ * Mount targets are visited in alphabetical order so first-wins is
20295
+ * deterministic across runs. In the current spec no group is shared across
20296
+ * mount targets; if one ever is, the alphabetically-first owner gets the
20297
+ * class and other dispatchers reference it by full path.
20298
+ */
20299
+ function buildGroupOwnerMap(ctx) {
20300
+ const owner = /* @__PURE__ */ new Map();
20301
+ const groups = groupByMount(ctx);
20302
+ const sortedTargets = [...groups.keys()].sort();
20303
+ for (const target of sortedTargets) {
20304
+ const g = groups.get(target);
20305
+ if (!g) continue;
20306
+ for (const op of g.operations) for (const grp of op.parameterGroups ?? []) if (!owner.has(grp.name)) owner.set(grp.name, target);
20307
+ }
20308
+ return owner;
20309
+ }
20310
+ /**
20311
+ * Collect all variant classes a given mount target owns. Variants are
20312
+ * inlined into the resource file (and its RBI counterpart) — Zeitwerk's
20313
+ * collapse convention means subdirectories under `lib/workos/<service>/`
20314
+ * don't add a namespace level, so files there can't define
20315
+ * `WorkOS::<Service>::<Variant>`. Inline definitions sidestep that.
20316
+ *
20317
+ * Variant parameter types are taken from the IR's leaf type. When the IR's
20318
+ * leaf is a bare primitive but the request body model has a richer type
20319
+ * (array/enum/model/map), we fall back to the body type to recover fidelity
20320
+ * the IR drops. Body nullability is stripped — when a parameter group is
20321
+ * optional, the body field for the group becomes nullable, but within a
20322
+ * variant the leaf is always required (selecting the variant means passing it).
20323
+ */
20324
+ function collectVariantsForMountTarget(ctx, models, mountTarget) {
20325
+ const owner = buildGroupOwnerMap(ctx);
20326
+ const seen = /* @__PURE__ */ new Set();
20327
+ const out = [];
20328
+ const g = groupByMount(ctx).get(mountTarget);
20329
+ if (!g) return out;
20330
+ for (const op of g.operations) {
20331
+ const bodyFieldTypes = collectBodyFieldTypes(op, models);
20332
+ for (const group of op.parameterGroups ?? []) {
20333
+ if (owner.get(group.name) !== mountTarget) continue;
20334
+ for (const variant of group.variants) {
20335
+ const cls = groupVariantClassName(group.name, variant.name);
20336
+ if (seen.has(cls)) continue;
20337
+ seen.add(cls);
20338
+ out.push({
20339
+ className: cls,
20340
+ groupName: group.name,
20341
+ variantName: variant.name,
20342
+ mountTarget,
20343
+ parameters: variant.parameters.map((p) => ({
20344
+ name: p.name,
20345
+ type: pickVariantParamType(p.type, bodyFieldTypes.get(p.name))
20346
+ }))
20347
+ });
20348
+ }
20349
+ }
20350
+ }
20351
+ return out;
20352
+ }
20353
+ /**
20354
+ * Pick the type for a variant leaf parameter.
20355
+ *
20356
+ * Prefer the IR's leaf type. Use the body model's type only when the IR is a
20357
+ * bare primitive but the body has a structured type — that's the original
20358
+ * fidelity-recovery case the body fallback was added for. Strip any outer
20359
+ * nullable from the body type, since body nullability reflects the parent
20360
+ * group's optionality, not the leaf's required-ness within the variant.
20361
+ *
20362
+ * Exported so the test emitter can recover the same type the variant class
20363
+ * declares — IR primitives for fields like `role_slugs` would otherwise stub
20364
+ * as `"stub"` strings instead of the `["stub"]` arrays the class accepts.
20365
+ */
20366
+ function pickVariantParamType(irType, bodyType) {
20367
+ if (!bodyType) return irType;
20368
+ const unwrappedBody = bodyType.kind === "nullable" ? bodyType.inner : bodyType;
20369
+ const bodyIsStructured = unwrappedBody.kind === "array" || unwrappedBody.kind === "enum" || unwrappedBody.kind === "model" || unwrappedBody.kind === "map";
20370
+ if (irType.kind === "primitive" && bodyIsStructured) return unwrappedBody;
20371
+ return irType;
20372
+ }
20373
+ function readableName(name) {
20374
+ return name.replace(/_/g, " ");
20375
+ }
20376
+ /**
20377
+ * Render the inline `Data.define` block for a single variant, indented for
20378
+ * inclusion inside a `class <Service>` body. Returns an array of lines with
20379
+ * 4-space indent (the resource file's class members are 4-space indented).
20380
+ */
20381
+ function emitInlineVariantClass(v) {
20382
+ const lines = [];
20383
+ lines.push(` # Identifies the ${readableName(v.groupName)} (${readableName(v.variantName)} variant).`);
20384
+ lines.push(" #");
20385
+ for (const p of v.parameters) {
20386
+ const yardType = mapTypeRefForYard(p.type);
20387
+ lines.push(` # @!attribute [r] ${fieldName(p.name)}`);
20388
+ lines.push(` # @return [${yardType}]`);
20389
+ }
20390
+ if (v.parameters.length === 0) lines.push(` ${v.className} = Data.define`);
20391
+ else {
20392
+ const fields = v.parameters.map((p) => `:${fieldName(p.name)}`).join(", ");
20393
+ lines.push(` ${v.className} = Data.define(${fields})`);
20394
+ }
20395
+ return lines;
20396
+ }
20397
+ /**
20398
+ * Render the inline RBI `class` block for a single variant, indented for
20399
+ * inclusion inside a `class <Service>` body in a service .rbi file. Returns
20400
+ * lines with 4-space indent.
20401
+ */
20402
+ function emitInlineVariantRbi(v) {
20403
+ const lines = [];
20404
+ const fqcn = `WorkOS::${v.mountTarget}::${v.className}`;
20405
+ lines.push(` class ${v.className}`);
20406
+ for (const p of v.parameters) {
20407
+ lines.push(` sig { returns(${mapSorbetType$1(p.type)}) }`);
20408
+ lines.push(` def ${fieldName(p.name)}; end`);
20409
+ lines.push("");
20410
+ }
20411
+ if (v.parameters.length === 0) {
20412
+ lines.push(` sig { returns(${fqcn}) }`);
20413
+ lines.push(` def self.new; end`);
20414
+ } else {
20415
+ lines.push(" sig do");
20416
+ lines.push(" params(");
20417
+ for (let i = 0; i < v.parameters.length; i++) {
20418
+ const p = v.parameters[i];
20419
+ const sep = i === v.parameters.length - 1 ? "" : ",";
20420
+ lines.push(` ${fieldName(p.name)}: ${mapSorbetType$1(p.type)}${sep}`);
20421
+ }
20422
+ lines.push(` ).returns(${fqcn})`);
20423
+ lines.push(" end");
20424
+ const kwargs = v.parameters.map((p) => `${fieldName(p.name)}:`).join(", ");
20425
+ lines.push(` def self.new(${kwargs}); end`);
20426
+ }
20427
+ lines.push(" end");
20428
+ return lines;
20429
+ }
20430
+ //#endregion
20236
20431
  //#region src/ruby/resources.ts
20237
20432
  /**
20238
20433
  * Generate Ruby resource (service) classes from IR services.
@@ -20249,6 +20444,7 @@ function generateResources(services, ctx) {
20249
20444
  for (const m of ctx.spec.models) modelByName.set(m.name, m);
20250
20445
  const listWrapperModels = /* @__PURE__ */ new Map();
20251
20446
  for (const m of ctx.spec.models) if (isListWrapperModel(m)) listWrapperModels.set(m.name, m);
20447
+ const groupOwners = buildGroupOwnerMap(ctx);
20252
20448
  for (const [mountTarget, group] of groups) {
20253
20449
  const cls = className(mountTarget);
20254
20450
  const file = fileName(mountTarget);
@@ -20278,7 +20474,8 @@ function generateResources(services, ctx) {
20278
20474
  modelNames,
20279
20475
  modelByName,
20280
20476
  listWrapperModels,
20281
- requires
20477
+ requires,
20478
+ groupOwners
20282
20479
  });
20283
20480
  methodBodies.push(body);
20284
20481
  if (resolved?.wrappers && resolved.wrappers.length > 0) {
@@ -20299,6 +20496,11 @@ function generateResources(services, ctx) {
20299
20496
  }
20300
20497
  lines.push("module WorkOS");
20301
20498
  lines.push(` class ${cls}`);
20499
+ const variants = collectVariantsForMountTarget(ctx, ctx.spec.models, mountTarget);
20500
+ for (const v of variants) {
20501
+ for (const line of emitInlineVariantClass(v)) lines.push(line);
20502
+ lines.push("");
20503
+ }
20302
20504
  lines.push(" def initialize(client)");
20303
20505
  lines.push(" @client = client");
20304
20506
  lines.push(" end");
@@ -20319,13 +20521,19 @@ function generateResources(services, ctx) {
20319
20521
  }
20320
20522
  /** Build a single Ruby method from an Operation. */
20321
20523
  function emitMethod(args) {
20322
- const { op, method, defaults, inferFromClient, hiddenParams, enumNames, modelNames, modelByName, listWrapperModels, requires } = args;
20524
+ const { op, method, defaults, inferFromClient, hiddenParams, enumNames, modelNames, modelByName, listWrapperModels, requires, groupOwners } = args;
20525
+ /** Fully-qualified Ruby constant for a variant (e.g. WorkOS::UserManagement::PasswordPlaintext). */
20526
+ const variantClassRef = (group, variantName) => {
20527
+ const owner = groupOwners.get(group.name);
20528
+ if (!owner) throw new Error(`No owner mount target found for parameter group '${group.name}'`);
20529
+ return scopedGroupVariantClassName(owner, group.name, variantName);
20530
+ };
20323
20531
  planOperation(op);
20324
20532
  const lines = [];
20325
20533
  const pathParams = op.pathParams ?? [];
20326
20534
  const groupedParamNames = collectGroupedParamNames(op);
20327
20535
  const queryParams = (op.queryParams ?? []).filter((q) => !groupedParamNames.has(q.name));
20328
- const bodyFields = getRequestBodyFields(op, hiddenParams, modelByName);
20536
+ const bodyFields = getRequestBodyFields(op, hiddenParams, modelByName).filter((f) => !groupedParamNames.has(f.name));
20329
20537
  const pathParamNames = new Set(pathParams.map((p) => safeParamName(p.name)));
20330
20538
  const bodyFieldRenames = /* @__PURE__ */ new Map();
20331
20539
  for (const f of bodyFields) {
@@ -20393,7 +20601,7 @@ function emitMethod(args) {
20393
20601
  sigParts.push(`${n}: nil`);
20394
20602
  }
20395
20603
  sigParts.push("request_options: {}");
20396
- const doc = buildYardDoc(op, pathParams, queryParams, bodyFields, hiddenParams, bodyFieldRenames, listWrapperModels);
20604
+ const doc = buildYardDoc(op, pathParams, queryParams, bodyFields, hiddenParams, bodyFieldRenames, listWrapperModels, variantClassRef);
20397
20605
  for (const line of doc) lines.push(` ${line}`);
20398
20606
  if (sigParts.length === 0) lines.push(` def ${method}`);
20399
20607
  else if (sigParts.length === 1 && sigParts[0].length < 60) lines.push(` def ${method}(${sigParts[0]})`);
@@ -20419,28 +20627,32 @@ function emitMethod(args) {
20419
20627
  const groupsGoToQuery = hasGroups && !hasBodyMethod;
20420
20628
  const hasQuery = qEntries.length > 0 || groupsGoToQuery;
20421
20629
  if (hasQuery) {
20630
+ const queryCompact = qEntries.some((q) => !q.required) ? ".compact" : "";
20422
20631
  lines.push(" params = {");
20423
20632
  for (let i = 0; i < qEntries.length; i++) {
20424
20633
  const q = qEntries[i];
20425
20634
  const sep = i === qEntries.length - 1 && !groupsGoToQuery ? "" : ",";
20426
20635
  lines.push(` ${rubyStringLit(q.name)} => ${safeParamName(q.name)}${sep}`);
20427
20636
  }
20428
- lines.push(" }.compact");
20637
+ lines.push(` }${queryCompact}`);
20429
20638
  if (groupsGoToQuery) for (const group of op.parameterGroups ?? []) {
20430
20639
  const prop = fieldName(group.name);
20431
20640
  if (group.optional) {
20432
20641
  lines.push(` if ${prop}`);
20433
- lines.push(` case ${prop}[:type]`);
20434
- } else lines.push(` case ${prop}[:type]`);
20642
+ lines.push(` case ${prop}`);
20643
+ } else lines.push(` case ${prop}`);
20435
20644
  for (const variant of group.variants) {
20436
- lines.push(` when ${rubyStringLit(variant.name)}`);
20437
- for (const p of variant.parameters) lines.push(` params[${rubyStringLit(p.name)}] = ${prop}[:${fieldName(p.name)}]`);
20645
+ const variantClass = variantClassRef(group, variant.name);
20646
+ lines.push(` when ${variantClass}`);
20647
+ for (const p of variant.parameters) lines.push(` params[${rubyStringLit(p.name)}] = ${prop}.${fieldName(p.name)}`);
20438
20648
  }
20649
+ lines.push(` else`);
20650
+ lines.push(` raise ArgumentError, ${dispatchErrorLiteral(group, prop, variantClassRef)}`);
20439
20651
  lines.push(" end");
20440
20652
  if (group.optional) lines.push(" end");
20441
20653
  }
20442
20654
  }
20443
- const hasBody = bodyFields.length > 0 && !["get", "head"].includes(method_http);
20655
+ const hasBody = bodyFields.length > 0 && !["get", "head"].includes(method_http) || hasGroups && hasBodyMethod;
20444
20656
  if (hasBody) {
20445
20657
  const bodyEntries = [];
20446
20658
  for (const [k, v] of Object.entries(defaults)) {
@@ -20452,26 +20664,32 @@ function emitMethod(args) {
20452
20664
  const optKey = fc === "client_secret" ? "api_key" : fc;
20453
20665
  bodyEntries.push(`${rubyStringLit(fc)} => (request_options[:${optKey}] || @client.${clientProp})`);
20454
20666
  }
20667
+ let bodyHasNilable = false;
20455
20668
  for (const f of bodyFields) {
20456
20669
  if (hiddenParams.has(f.name)) continue;
20457
20670
  bodyEntries.push(`${rubyStringLit(f.name)} => ${bodyKwargName(f.name)}`);
20671
+ if (!f.required) bodyHasNilable = true;
20458
20672
  }
20673
+ const bodyCompact = bodyHasNilable ? ".compact" : "";
20459
20674
  lines.push(" body = {");
20460
20675
  for (let i = 0; i < bodyEntries.length; i++) {
20461
20676
  const sep = i === bodyEntries.length - 1 ? "" : ",";
20462
20677
  lines.push(` ${bodyEntries[i]}${sep}`);
20463
20678
  }
20464
- lines.push(" }.compact");
20679
+ lines.push(` }${bodyCompact}`);
20465
20680
  if (hasGroups && hasBodyMethod) for (const group of op.parameterGroups ?? []) {
20466
20681
  const prop = fieldName(group.name);
20467
20682
  if (group.optional) {
20468
20683
  lines.push(` if ${prop}`);
20469
- lines.push(` case ${prop}[:type]`);
20470
- } else lines.push(` case ${prop}[:type]`);
20684
+ lines.push(` case ${prop}`);
20685
+ } else lines.push(` case ${prop}`);
20471
20686
  for (const variant of group.variants) {
20472
- lines.push(` when ${rubyStringLit(variant.name)}`);
20473
- for (const p of variant.parameters) lines.push(` body[${rubyStringLit(p.name)}] = ${prop}[:${fieldName(p.name)}]`);
20687
+ const variantClass = variantClassRef(group, variant.name);
20688
+ lines.push(` when ${variantClass}`);
20689
+ for (const p of variant.parameters) lines.push(` body[${rubyStringLit(p.name)}] = ${prop}.${fieldName(p.name)}`);
20474
20690
  }
20691
+ lines.push(` else`);
20692
+ lines.push(` raise ArgumentError, ${dispatchErrorLiteral(group, prop, variantClassRef)}`);
20475
20693
  lines.push(" end");
20476
20694
  if (group.optional) lines.push(" end");
20477
20695
  }
@@ -20686,7 +20904,7 @@ function oneLine(desc) {
20686
20904
  if (!desc) return "";
20687
20905
  return desc.replace(/\r/g, " ").replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
20688
20906
  }
20689
- function buildYardDoc(op, pathParams, queryParams, bodyFields, hiddenParams, bodyFieldRenames, listWrapperModels) {
20907
+ function buildYardDoc(op, pathParams, queryParams, bodyFields, hiddenParams, bodyFieldRenames, listWrapperModels, variantClassRef) {
20690
20908
  const lines = [];
20691
20909
  const firstLine = (op.description ?? `${op.httpMethod.toUpperCase()} ${op.path}`).split("\n")[0] ?? "";
20692
20910
  lines.push(`# ${firstLine}`);
@@ -20722,6 +20940,14 @@ function buildYardDoc(op, pathParams, queryParams, bodyFields, hiddenParams, bod
20722
20940
  const deprecatedPrefix = q.deprecated ? "(deprecated) " : "";
20723
20941
  lines.push(`# @param ${n} [${type}${suffix}] ${deprecatedPrefix}${oneLine(q.description)}`.trim());
20724
20942
  }
20943
+ for (const group of op.parameterGroups ?? []) {
20944
+ const n = fieldName(group.name);
20945
+ if (emittedParamNames.has(n)) continue;
20946
+ emittedParamNames.add(n);
20947
+ const variantTypes = group.variants.map((v) => variantClassRef(group, v.name)).join(", ");
20948
+ const suffix = group.optional ? ", nil" : "";
20949
+ lines.push(`# @param ${n} [${variantTypes}${suffix}] Identifies the ${group.name.replace(/_/g, " ")}.`);
20950
+ }
20725
20951
  lines.push(`# @param request_options [Hash] (see WorkOS::Types::RequestOptions)`);
20726
20952
  const ref = op.response;
20727
20953
  if (ref.kind === "primitive" && ref.type === "unknown") lines.push(`# @return [void]`);
@@ -20744,6 +20970,14 @@ function buildYardDoc(op, pathParams, queryParams, bodyFields, hiddenParams, bod
20744
20970
  function rubyStringLit(s) {
20745
20971
  return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
20746
20972
  }
20973
+ /**
20974
+ * Build a Ruby double-quoted string expression for the `else raise ArgumentError`
20975
+ * arm of a parameter-group dispatcher. Lists the expected variant classes and
20976
+ * interpolates the actual class of the value the caller passed.
20977
+ */
20978
+ function dispatchErrorLiteral(group, prop, variantClassRef) {
20979
+ return `"expected ${prop} to be one of: ${group.variants.map((v) => variantClassRef(group, v.name)).join(", ")}, got #{${prop}.class}"`;
20980
+ }
20747
20981
  //#endregion
20748
20982
  //#region src/ruby/client.ts
20749
20983
  /**
@@ -20912,7 +21146,7 @@ function generateMainEntryFile(spec, ctx) {
20912
21146
  * keep the generated namespace flat while the filesystem is grouped.
20913
21147
  */
20914
21148
  function collectModelSubdirs(spec, ctx) {
20915
- const modelToService = assignModelsToServices(spec.models, spec.services);
21149
+ const modelToService = assignModelsToServices(spec.models, spec.services, ctx.modelHints);
20916
21150
  const mountDirMap = buildMountDirMap(ctx);
20917
21151
  const subdirs = /* @__PURE__ */ new Set();
20918
21152
  for (const model of spec.models) {
@@ -20973,9 +21207,11 @@ function generateClientClass(spec, ctx) {
20973
21207
  function generateTests(spec, ctx) {
20974
21208
  const files = [];
20975
21209
  const groups = groupByMount(ctx);
21210
+ const models = spec.models;
20976
21211
  const modelByName = /* @__PURE__ */ new Map();
20977
- for (const m of spec.models) modelByName.set(m.name, m);
21212
+ for (const m of models) modelByName.set(m.name, m);
20978
21213
  const lookup = buildResolvedLookup(ctx);
21214
+ const groupOwners = buildGroupOwnerMap(ctx);
20979
21215
  for (const [mountTarget, group] of groups) {
20980
21216
  const cls = className(mountTarget);
20981
21217
  const prop = servicePropertyName(mountTarget);
@@ -21004,7 +21240,9 @@ function generateTests(spec, ctx) {
21004
21240
  op.httpMethod.toLowerCase();
21005
21241
  const httpMethodSym = `:${op.httpMethod.toLowerCase()}`;
21006
21242
  const resolved = lookupResolved(op, lookup);
21007
- const callArgs = buildCallArgsStub(op, modelByName, buildHiddenParams$1(resolved));
21243
+ const hiddenParams = buildHiddenParams$1(resolved);
21244
+ const callArgs = buildCallArgsStub(op, modelByName, hiddenParams, groupOwners, models);
21245
+ const bodyMatcher = buildBodyMatcher(op, modelByName, hiddenParams, models);
21008
21246
  authMethodManifest.push({
21009
21247
  method,
21010
21248
  httpMethodSym,
@@ -21013,23 +21251,47 @@ function generateTests(spec, ctx) {
21013
21251
  });
21014
21252
  const stubRegex = stubUrlRegex(stubUrl);
21015
21253
  lines.push(` def test_${method}_returns_expected_result`);
21254
+ lines.push(` stub_request(${httpMethodSym}, ${stubRegex})`);
21255
+ if (bodyMatcher) lines.push(` .with(body: ${bodyMatcher})`);
21016
21256
  if (isList) {
21017
- lines.push(` stub_request(${httpMethodSym}, ${stubRegex})`);
21018
21257
  lines.push(` .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)`);
21019
21258
  lines.push(` result = @client.${prop}.${method}(${callArgs})`);
21020
21259
  lines.push(" assert_kind_of WorkOS::Types::ListStruct, result");
21021
21260
  } else if (op.response.kind === "primitive") {
21022
- lines.push(` stub_request(${httpMethodSym}, ${stubRegex})`);
21023
21261
  lines.push(` .to_return(body: "{}", status: 200)`);
21024
21262
  lines.push(` result = @client.${prop}.${method}(${callArgs})`);
21025
21263
  lines.push(" assert_nil result");
21026
21264
  } else {
21027
- lines.push(` stub_request(${httpMethodSym}, ${stubRegex})`);
21028
21265
  lines.push(` .to_return(body: "{}", status: 200)`);
21029
21266
  lines.push(` result = @client.${prop}.${method}(${callArgs})`);
21030
21267
  lines.push(" refute_nil result");
21031
21268
  }
21032
21269
  lines.push(" end");
21270
+ for (const group of op.parameterGroups ?? []) for (let vi = 1; vi < group.variants.length; vi++) {
21271
+ const variant = group.variants[vi];
21272
+ const overrides = new Map([[group.name, vi]]);
21273
+ const variantCallArgs = buildCallArgsStub(op, modelByName, hiddenParams, groupOwners, models, overrides);
21274
+ const variantBodyMatcher = buildBodyMatcher(op, modelByName, hiddenParams, models, overrides);
21275
+ const suffix = `with_${fieldName(group.name)}_${fieldName(variant.name)}`;
21276
+ lines.push("");
21277
+ lines.push(` def test_${method}_${suffix}_returns_expected_result`);
21278
+ lines.push(` stub_request(${httpMethodSym}, ${stubRegex})`);
21279
+ if (variantBodyMatcher) lines.push(` .with(body: ${variantBodyMatcher})`);
21280
+ if (isList) {
21281
+ lines.push(` .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)`);
21282
+ lines.push(` result = @client.${prop}.${method}(${variantCallArgs})`);
21283
+ lines.push(" assert_kind_of WorkOS::Types::ListStruct, result");
21284
+ } else if (op.response.kind === "primitive") {
21285
+ lines.push(` .to_return(body: "{}", status: 200)`);
21286
+ lines.push(` result = @client.${prop}.${method}(${variantCallArgs})`);
21287
+ lines.push(" assert_nil result");
21288
+ } else {
21289
+ lines.push(` .to_return(body: "{}", status: 200)`);
21290
+ lines.push(` result = @client.${prop}.${method}(${variantCallArgs})`);
21291
+ lines.push(" refute_nil result");
21292
+ }
21293
+ lines.push(" end");
21294
+ }
21033
21295
  if (resolved?.wrappers && resolved.wrappers.length > 0) for (const wrapper of resolved.wrappers) emitWrapperTests({
21034
21296
  lines,
21035
21297
  wrapper,
@@ -21160,8 +21422,12 @@ function roundTripStub(ref, enumNames) {
21160
21422
  default: return `nil`;
21161
21423
  }
21162
21424
  }
21163
- /** Build minimal placeholder arguments for calling the SDK method from a test. */
21164
- function buildCallArgsStub(op, modelByName, hiddenParams) {
21425
+ /** Build minimal placeholder arguments for calling the SDK method from a test.
21426
+ * `variantOverrides` selects a non-zero variant index per group; absent groups
21427
+ * default to variant 0. Used to emit per-variant test cases that exercise the
21428
+ * second/third arm of each parameter-group dispatcher.
21429
+ */
21430
+ function buildCallArgsStub(op, modelByName, hiddenParams, groupOwners, models, variantOverrides = /* @__PURE__ */ new Map()) {
21165
21431
  const parts = [];
21166
21432
  const seen = /* @__PURE__ */ new Set();
21167
21433
  const pathParamNames = /* @__PURE__ */ new Set();
@@ -21193,17 +21459,68 @@ function buildCallArgsStub(op, modelByName, hiddenParams) {
21193
21459
  seen.add(name);
21194
21460
  parts.push(`${name}: ${stubValueFor(q.type)}`);
21195
21461
  }
21462
+ const bodyFieldTypes = collectBodyFieldTypes(op, models);
21196
21463
  for (const group of op.parameterGroups ?? []) {
21197
- if (group.optional) continue;
21198
21464
  const name = fieldName(group.name);
21199
21465
  if (seen.has(name)) continue;
21200
21466
  seen.add(name);
21201
- const firstVariant = group.variants[0];
21202
- if (firstVariant) parts.push(`${name}: { type: "${firstVariant.name}" }`);
21203
- else parts.push(`${name}: {}`);
21467
+ const idx = variantOverrides.get(group.name) ?? 0;
21468
+ const variant = group.variants[idx];
21469
+ if (variant) {
21470
+ const owner = groupOwners.get(group.name);
21471
+ if (!owner) throw new Error(`No owner mount target found for parameter group '${group.name}'`);
21472
+ const variantClass = scopedGroupVariantClassName(owner, group.name, variant.name);
21473
+ const fieldStubs = variant.parameters.map((p) => `${fieldName(p.name)}: ${stubValueFor(pickVariantParamType(p.type, bodyFieldTypes.get(p.name)))}`).join(", ");
21474
+ parts.push(`${name}: ${variantClass}.new(${fieldStubs})`);
21475
+ }
21204
21476
  }
21205
21477
  return parts.join(", ");
21206
21478
  }
21479
+ /**
21480
+ * Build a Ruby `hash_including(...)` matcher describing the wire body the
21481
+ * SDK should send for an operation whose body is constructed (in part) by a
21482
+ * parameter-group dispatcher. Returns `null` for operations without body
21483
+ * groups — those are still stubbed without a body matcher.
21484
+ *
21485
+ * The matcher includes every required non-group body field plus the first
21486
+ * variant's wire-name leaves for each group dispatched into the body. This
21487
+ * catches regressions where the dispatcher silently drops a passed group
21488
+ * (the original `update_organization_membership` regression).
21489
+ */
21490
+ function buildBodyMatcher(op, modelByName, hiddenParams, models, variantOverrides = /* @__PURE__ */ new Map()) {
21491
+ const httpMethod = op.httpMethod.toLowerCase();
21492
+ const hasBodyMethod = ![
21493
+ "get",
21494
+ "head",
21495
+ "delete"
21496
+ ].includes(httpMethod);
21497
+ const hasGroups = (op.parameterGroups?.length ?? 0) > 0;
21498
+ if (!hasBodyMethod || !hasGroups) return null;
21499
+ const groupedParamNames = /* @__PURE__ */ new Set();
21500
+ for (const group of op.parameterGroups ?? []) for (const variant of group.variants) for (const p of variant.parameters) groupedParamNames.add(p.name);
21501
+ const entries = [];
21502
+ if (op.requestBody) {
21503
+ const bodyModel = resolveBodyModel(op.requestBody, modelByName);
21504
+ if (bodyModel) for (const f of bodyModel.fields) {
21505
+ if (!f.required) continue;
21506
+ if (hiddenParams.has(f.name)) continue;
21507
+ if (groupedParamNames.has(f.name)) continue;
21508
+ entries.push(`"${f.name}" => ${stubValueFor(f.type)}`);
21509
+ }
21510
+ }
21511
+ const bodyFieldTypes = collectBodyFieldTypes(op, models);
21512
+ for (const group of op.parameterGroups ?? []) {
21513
+ const idx = variantOverrides.get(group.name) ?? 0;
21514
+ const variant = group.variants[idx];
21515
+ if (!variant) continue;
21516
+ for (const p of variant.parameters) {
21517
+ const recovered = pickVariantParamType(p.type, bodyFieldTypes.get(p.name));
21518
+ entries.push(`"${p.name}" => ${stubValueFor(recovered)}`);
21519
+ }
21520
+ }
21521
+ if (entries.length === 0) return null;
21522
+ return `hash_including(${entries.join(", ")})`;
21523
+ }
21207
21524
  function resolveBodyModel(ref, modelByName) {
21208
21525
  if (ref.kind === "model") return modelByName.get(ref.name) ?? null;
21209
21526
  if (ref.kind === "nullable") return resolveBodyModel(ref.inner, modelByName);
@@ -21257,7 +21574,7 @@ function stubValueFor(ref) {
21257
21574
  case "boolean": return `true`;
21258
21575
  default: return `nil`;
21259
21576
  }
21260
- case "array": return `[]`;
21577
+ case "array": return `[${stubValueFor(ref.items)}]`;
21261
21578
  case "map": return `{}`;
21262
21579
  case "enum": return `"stub"`;
21263
21580
  case "literal":
@@ -21392,6 +21709,7 @@ function generateRbiFiles(spec, ctx) {
21392
21709
  for (const m of spec.models) modelByName.set(m.name, m);
21393
21710
  const listWrapperModels = /* @__PURE__ */ new Map();
21394
21711
  for (const m of spec.models) if (isListWrapperModel(m)) listWrapperModels.set(m.name, m);
21712
+ const groupOwners = buildGroupOwnerMap(ctx);
21395
21713
  for (const [mountTarget, group] of groups) {
21396
21714
  const cls = className(mountTarget);
21397
21715
  const lines = [];
@@ -21399,6 +21717,11 @@ function generateRbiFiles(spec, ctx) {
21399
21717
  lines.push("");
21400
21718
  lines.push("module WorkOS");
21401
21719
  lines.push(` class ${cls}`);
21720
+ const variants = collectVariantsForMountTarget(ctx, spec.models, mountTarget);
21721
+ for (const v of variants) {
21722
+ for (const line of emitInlineVariantRbi(v)) lines.push(line);
21723
+ lines.push("");
21724
+ }
21402
21725
  lines.push(" sig { params(client: WorkOS::BaseClient).void }");
21403
21726
  lines.push(" def initialize(client); end");
21404
21727
  lines.push("");
@@ -21415,7 +21738,15 @@ function generateRbiFiles(spec, ctx) {
21415
21738
  const hiddenParams = buildHiddenParams$1(resolved);
21416
21739
  const groupedParamNames = collectGroupedParamNames(op);
21417
21740
  const queryParams = (op.queryParams ?? []).filter((q) => !groupedParamNames.has(q.name));
21418
- const bodyFields = getRequestBodyFieldsFlat(op, hiddenParams, modelByName);
21741
+ const bodyFields = getRequestBodyFieldsFlat(op, hiddenParams, modelByName).filter((f) => !groupedParamNames.has(f.name));
21742
+ const parameterGroups = op.parameterGroups ?? [];
21743
+ const groupSorbetType = (group) => {
21744
+ const owner = groupOwners.get(group.name);
21745
+ if (!owner) throw new Error(`No owner mount target found for parameter group '${group.name}'`);
21746
+ const variants = group.variants.map((v) => scopedGroupVariantClassName(owner, group.name, v.name));
21747
+ if (variants.length === 1) return variants[0];
21748
+ return `T.any(${variants.join(", ")})`;
21749
+ };
21419
21750
  const sigParams = [];
21420
21751
  const seen = /* @__PURE__ */ new Set();
21421
21752
  for (const p of op.pathParams ?? []) {
@@ -21440,6 +21771,13 @@ function generateRbiFiles(spec, ctx) {
21440
21771
  seen.add(n);
21441
21772
  sigParams.push(`${n}: ${mapSorbetType(q.type)}`);
21442
21773
  }
21774
+ for (const group of parameterGroups) {
21775
+ if (group.optional) continue;
21776
+ const n = fieldName(group.name);
21777
+ if (seen.has(n)) continue;
21778
+ seen.add(n);
21779
+ sigParams.push(`${n}: ${groupSorbetType(group)}`);
21780
+ }
21443
21781
  for (const f of bodyFields) {
21444
21782
  if (hiddenParams.has(f.name)) continue;
21445
21783
  if (f.required) continue;
@@ -21456,6 +21794,13 @@ function generateRbiFiles(spec, ctx) {
21456
21794
  seen.add(n);
21457
21795
  sigParams.push(`${n}: T.nilable(${unwrapNilable(mapSorbetType(q.type))})`);
21458
21796
  }
21797
+ for (const group of parameterGroups) {
21798
+ if (!group.optional) continue;
21799
+ const n = fieldName(group.name);
21800
+ if (seen.has(n)) continue;
21801
+ seen.add(n);
21802
+ sigParams.push(`${n}: T.nilable(${groupSorbetType(group)})`);
21803
+ }
21459
21804
  sigParams.push("request_options: T::Hash[Symbol, T.untyped]");
21460
21805
  const retType = mapSorbetReturnType(op.response, listWrapperModels, modelNames);
21461
21806
  lines.push(" sig do");
@@ -21630,4 +21975,4 @@ const workosEmittersPlugin = {
21630
21975
  //#endregion
21631
21976
  export { nodeEmitter as _, rustExtractor as a, pythonExtractor as c, rubyEmitter as d, kotlinEmitter as f, pythonEmitter as g, phpEmitter as h, kotlinExtractor as i, rubyExtractor as l, goEmitter as m, elixirExtractor as n, goExtractor as o, dotnetEmitter as p, dotnetExtractor as r, phpExtractor as s, workosEmittersPlugin as t, nodeExtractor as u };
21632
21977
 
21633
- //# sourceMappingURL=plugin-Bk0xWTQC.mjs.map
21978
+ //# sourceMappingURL=plugin-Bp46oZIh.mjs.map