@workos/oagen-emitters 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2450,17 +2450,9 @@ function dump$1(input, options) {
2450
2450
  return "";
2451
2451
  }
2452
2452
  var dumper = { dump: dump$1 };
2453
- function renamed(from, to) {
2454
- return function() {
2455
- throw new Error("Function yaml." + from + " is removed in js-yaml 4. Use yaml." + to + " instead, which is now safe by default.");
2456
- };
2457
- }
2458
2453
  var load = loader.load;
2459
2454
  loader.loadAll;
2460
2455
  dumper.dump;
2461
- renamed("safeLoad", "load");
2462
- renamed("safeLoadAll", "loadAll");
2463
- renamed("safeDump", "dump");
2464
2456
  //#endregion
2465
2457
  //#region src/shared/model-utils.ts
2466
2458
  /**
@@ -6356,7 +6348,7 @@ function renderVoidTest(lines, op, plan, method, serviceProp, modelMap) {
6356
6348
  lines.push(" });");
6357
6349
  }
6358
6350
  function renderErrorTest(lines, op, plan, method, serviceProp, modelMap) {
6359
- const args = buildCallArgs(op, plan, modelMap);
6351
+ const args = buildCallArgs$1(op, plan, modelMap);
6360
6352
  lines.push("");
6361
6353
  lines.push(` testUnauthorized(() => workos.${serviceProp}.${method}(${args}));`);
6362
6354
  const errorStatuses = new Set(op.errors.map((e) => e.statusCode));
@@ -6379,7 +6371,7 @@ function renderErrorTest(lines, op, plan, method, serviceProp, modelMap) {
6379
6371
  * Build the argument string for a method call in tests.
6380
6372
  * Shared by renderErrorTest and other test renderers.
6381
6373
  */
6382
- function buildCallArgs(op, plan, modelMap) {
6374
+ function buildCallArgs$1(op, plan, modelMap) {
6383
6375
  const pathArgs = buildTestPathArgs(op);
6384
6376
  const isPaginated = plan.isPaginated;
6385
6377
  const hasBody = plan.hasBody;
@@ -23562,7 +23554,12 @@ function resolveFieldNames(fields) {
23562
23554
  }
23563
23555
  function renderField(field, rustField, modelName, registry) {
23564
23556
  const lines = [];
23565
- if (field.description) for (const c of docComment$1(field.description)) lines.push(` ${c}`);
23557
+ const hasDescription = !!field.description;
23558
+ if (hasDescription) for (const c of docComment$1(field.description)) lines.push(` ${c}`);
23559
+ if (field.default != null) {
23560
+ if (hasDescription) lines.push(" ///");
23561
+ lines.push(` /// Defaults to \`${formatDefault$1(field.default)}\`.`);
23562
+ }
23566
23563
  const rename = rustField !== field.name ? field.name : null;
23567
23564
  let baseType = mapTypeRef$1(field.type, {
23568
23565
  hint: `${typeName(modelName)}${typeName(field.name)}`,
@@ -23587,6 +23584,15 @@ function renderModelsBarrel(modules) {
23587
23584
  function docComment$1(text) {
23588
23585
  return text.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((l) => `/// ${l}`);
23589
23586
  }
23587
+ /**
23588
+ * Render a spec-level default value for inclusion in a doc comment. Strings
23589
+ * render bare (e.g. `desc`) so they nest naturally inside the surrounding
23590
+ * backticks; numbers/booleans use JSON encoding.
23591
+ */
23592
+ function formatDefault$1(value) {
23593
+ if (typeof value === "string") return value;
23594
+ return JSON.stringify(value);
23595
+ }
23590
23596
  //#endregion
23591
23597
  //#region src/rust/enums.ts
23592
23598
  /**
@@ -23807,6 +23813,7 @@ function renderMountGroup(mountName, resolvedOps, ctx, registry, _lookup) {
23807
23813
  lines.push(" pub(crate) client: &'a Client,");
23808
23814
  lines.push("}");
23809
23815
  lines.push("");
23816
+ const groupEmitter = new GroupEmitter();
23810
23817
  const paramsStructs = [];
23811
23818
  const methods = [];
23812
23819
  const seenMethods = /* @__PURE__ */ new Set();
@@ -23819,7 +23826,7 @@ function renderMountGroup(mountName, resolvedOps, ctx, registry, _lookup) {
23819
23826
  seenMethods.add(wrapperMethodName);
23820
23827
  const paramsType = `${typeName(wrapper.name)}Params`;
23821
23828
  const params = resolveWrapperParams(wrapper, ctx);
23822
- paramsStructs.push(renderWrapperParamsStruct(paramsType, op, wrapper, params, registry));
23829
+ paramsStructs.push(renderWrapperParamsStruct(paramsType, op, wrapper, params, registry, ctx));
23823
23830
  methods.push(renderWrapperMethod(op, wrapper, params, paramsType, wrapperMethodName));
23824
23831
  }
23825
23832
  continue;
@@ -23827,13 +23834,25 @@ function renderMountGroup(mountName, resolvedOps, ctx, registry, _lookup) {
23827
23834
  const m = methodName(resolved.methodName);
23828
23835
  if (seenMethods.has(m)) continue;
23829
23836
  seenMethods.add(m);
23837
+ if (resolved.urlBuilder) {
23838
+ const paramsType = `${typeName(resolved.methodName)}Params`;
23839
+ const emptyParams = isEmptyParams(op, resolved);
23840
+ if (!emptyParams) paramsStructs.push(renderParamsStruct(paramsType, op, resolved, registry, ctx, groupEmitter));
23841
+ methods.push(renderUrlBuilderMethod(op, resolved, paramsType, m, emptyParams));
23842
+ continue;
23843
+ }
23830
23844
  const paramsType = `${typeName(resolved.methodName)}Params`;
23831
23845
  const emptyParams = isEmptyParams(op, resolved);
23832
- if (!emptyParams) paramsStructs.push(renderParamsStruct(paramsType, op, resolved, registry));
23846
+ if (!emptyParams) paramsStructs.push(renderParamsStruct(paramsType, op, resolved, registry, ctx, groupEmitter));
23833
23847
  methods.push(renderMethod(op, resolved, paramsType, m, emptyParams));
23834
23848
  const autoPaging = renderAutoPagingMethod(op, resolved, paramsType, m, ctx);
23835
23849
  if (autoPaging) methods.push(autoPaging);
23836
23850
  }
23851
+ const groupBlock = groupEmitter.render();
23852
+ if (groupBlock.length > 0) {
23853
+ lines.push(groupBlock);
23854
+ lines.push("");
23855
+ }
23837
23856
  for (const s of paramsStructs) {
23838
23857
  lines.push(s);
23839
23858
  lines.push("");
@@ -23846,14 +23865,158 @@ function renderMountGroup(mountName, resolvedOps, ctx, registry, _lookup) {
23846
23865
  lines.push("}");
23847
23866
  return lines.join("\n").replace(/\n+$/g, "\n");
23848
23867
  }
23849
- function renderParamsStruct(name, op, resolved, registry) {
23868
+ var GroupEmitter = class {
23869
+ enums = [];
23870
+ bodies = [];
23871
+ seenEnums = /* @__PURE__ */ new Set();
23872
+ seenBodies = /* @__PURE__ */ new Set();
23873
+ /** Register a parameter-group enum, returning the PascalCase Rust name. */
23874
+ registerEnum(group) {
23875
+ const name = typeName(group.name);
23876
+ const variants = group.variants.map((v) => ({
23877
+ name: typeName(v.name),
23878
+ fields: v.parameters.map((p) => ({
23879
+ rustName: fieldName(p.name),
23880
+ wireName: p.name,
23881
+ rustType: rustTypeForGroupParam(p.type)
23882
+ }))
23883
+ }));
23884
+ if (!this.seenEnums.has(name)) {
23885
+ this.seenEnums.add(name);
23886
+ this.enums.push({
23887
+ name,
23888
+ variants
23889
+ });
23890
+ }
23891
+ return name;
23892
+ }
23893
+ /** Register a synthetic body struct, returning its PascalCase Rust name. */
23894
+ registerBody(spec) {
23895
+ if (!this.seenBodies.has(spec.name)) {
23896
+ this.seenBodies.add(spec.name);
23897
+ this.bodies.push(spec);
23898
+ }
23899
+ return spec.name;
23900
+ }
23901
+ render() {
23902
+ const blocks = [];
23903
+ for (const e of this.enums) {
23904
+ const lines = [];
23905
+ lines.push("#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]");
23906
+ lines.push("#[serde(untagged)]");
23907
+ lines.push(`pub enum ${e.name} {`);
23908
+ for (const v of e.variants) {
23909
+ if (v.fields.length === 0) {
23910
+ lines.push(` ${v.name},`);
23911
+ continue;
23912
+ }
23913
+ lines.push(` ${v.name} {`);
23914
+ for (const f of v.fields) {
23915
+ if (f.rustName !== f.wireName) lines.push(` #[serde(rename = ${JSON.stringify(f.wireName)})]`);
23916
+ lines.push(` ${f.rustName}: ${f.rustType},`);
23917
+ }
23918
+ lines.push(" },");
23919
+ }
23920
+ lines.push("}");
23921
+ blocks.push(lines.join("\n"));
23922
+ }
23923
+ for (const b of this.bodies) {
23924
+ const lines = [];
23925
+ const everythingOptional = b.flatFields.every((f) => !f.required) && b.flattenEnums.every((f) => !f.required);
23926
+ const baseDerives = "Debug, Clone, serde::Serialize, serde::Deserialize";
23927
+ const derives = everythingOptional ? `${baseDerives}, Default` : baseDerives;
23928
+ lines.push(`#[derive(${derives})]`);
23929
+ lines.push(`pub struct ${b.name} {`);
23930
+ for (const f of b.flatFields) {
23931
+ if (f.doc) for (const c of paramDocComment(f.doc)) lines.push(` ${c}`);
23932
+ if (f.required) {
23933
+ if (f.doc) lines.push(" ///");
23934
+ lines.push(" /// Required.");
23935
+ }
23936
+ if (!f.required) lines.push(" #[serde(skip_serializing_if = \"Option::is_none\")]");
23937
+ if (f.rustName !== f.wireName) lines.push(` #[serde(rename = ${JSON.stringify(f.wireName)})]`);
23938
+ lines.push(` pub ${f.rustName}: ${f.rustType},`);
23939
+ }
23940
+ for (const f of b.flattenEnums) {
23941
+ if (f.doc) for (const c of paramDocComment(f.doc)) lines.push(` ${c}`);
23942
+ lines.push(" #[serde(flatten)]");
23943
+ if (!f.required) lines.push(" #[serde(skip_serializing_if = \"Option::is_none\")]");
23944
+ lines.push(` pub ${f.rustName}: ${f.rustType},`);
23945
+ }
23946
+ lines.push("}");
23947
+ const required = [...b.flatFields.filter((f) => f.required), ...b.flattenEnums.filter((f) => f.required)];
23948
+ if (required.length > 0 || b.flatFields.length + b.flattenEnums.length > 0) {
23949
+ const ctorArgs = required.map((f) => `${f.rustName}: ${ctorParamType(f.rustType)}`).join(", ");
23950
+ const init = [];
23951
+ for (const f of b.flatFields) if (f.required) {
23952
+ const value = ctorParamConvert(f.rustType, f.rustName);
23953
+ init.push(value === f.rustName ? ` ${f.rustName},` : ` ${f.rustName}: ${value},`);
23954
+ } else init.push(` ${f.rustName}: Default::default(),`);
23955
+ for (const f of b.flattenEnums) if (f.required) {
23956
+ const value = ctorParamConvert(f.rustType, f.rustName);
23957
+ init.push(value === f.rustName ? ` ${f.rustName},` : ` ${f.rustName}: ${value},`);
23958
+ } else init.push(` ${f.rustName}: Default::default(),`);
23959
+ lines.push("");
23960
+ lines.push(`impl ${b.name} {`);
23961
+ lines.push(` /// Construct a new \`${b.name}\` with the required fields set.`);
23962
+ lines.push(` pub fn new(${ctorArgs}) -> Self {`);
23963
+ lines.push(" Self {");
23964
+ for (const l of init) lines.push(l);
23965
+ lines.push(" }");
23966
+ lines.push(" }");
23967
+ lines.push("}");
23968
+ }
23969
+ blocks.push(lines.join("\n"));
23970
+ }
23971
+ return blocks.join("\n\n");
23972
+ }
23973
+ };
23974
+ /**
23975
+ * Render the Rust type for a parameter-group variant field. Variants commit
23976
+ * the caller to supplying their full payload, so optional individual params
23977
+ * still flow as `String` (not `Option<String>`); the enum-level choice is the
23978
+ * one source of truth for "did the caller pick this variant or not."
23979
+ */
23980
+ function rustTypeForGroupParam(type) {
23981
+ const rust = mapTypeRef$1(type);
23982
+ if (rust.startsWith("Option<")) return rust.slice(7, -1);
23983
+ return rust;
23984
+ }
23985
+ /** Classify each parameter group on an op as "query" or "body". */
23986
+ function classifyGroup(group, op) {
23987
+ const queryNames = new Set(op.queryParams.map((qp) => qp.name));
23988
+ return group.variants.every((v) => v.parameters.every((p) => queryNames.has(p.name))) ? "query" : "body";
23989
+ }
23990
+ function renderParamsStruct(name, op, resolved, registry, ctx, groupEmitter) {
23850
23991
  const bodyRequired = isBodyRequired(op);
23851
23992
  const hidden = new Set([...Object.keys(resolved.defaults ?? {}), ...resolved.inferFromClient ?? []]);
23993
+ const queryGroupParamNames = /* @__PURE__ */ new Set();
23994
+ const bodyGroupParamNames = /* @__PURE__ */ new Set();
23995
+ const queryGroupFields = [];
23996
+ const bodyGroupFields = [];
23997
+ for (const group of op.parameterGroups ?? []) {
23998
+ const enumName = groupEmitter.registerEnum(group);
23999
+ const rustType = group.optional ? `Option<${enumName}>` : enumName;
24000
+ const groupField = {
24001
+ name: fieldName(group.name),
24002
+ rustType,
24003
+ required: !group.optional,
24004
+ doc: void 0
24005
+ };
24006
+ if (classifyGroup(group, op) === "query") {
24007
+ for (const v of group.variants) for (const p of v.parameters) queryGroupParamNames.add(p.name);
24008
+ queryGroupFields.push(groupField);
24009
+ } else {
24010
+ for (const v of group.variants) for (const p of v.parameters) bodyGroupParamNames.add(p.name);
24011
+ bodyGroupFields.push(groupField);
24012
+ }
24013
+ }
23852
24014
  const fields = [];
23853
24015
  const fieldLines = [];
23854
24016
  const seen = /* @__PURE__ */ new Set();
23855
- const emitField = (p) => {
24017
+ const emitField = (p, opts = {}) => {
23856
24018
  if (hidden.has(p.name)) return;
24019
+ if (queryGroupParamNames.has(p.name)) return;
23857
24020
  const fname = fieldName(p.name);
23858
24021
  if (seen.has(fname)) return;
23859
24022
  seen.add(fname);
@@ -23863,26 +24026,47 @@ function renderParamsStruct(name, op, resolved, registry) {
23863
24026
  });
23864
24027
  if (!p.required && !rust.startsWith("Option<")) rust = makeOptional(rust);
23865
24028
  rust = applySecretRedaction(rust, p.name);
24029
+ const defaultExpr = p.default != null ? rustDefaultExpr(p.default, p.type, rust.startsWith("Option<"), ctx) : null;
23866
24030
  const desc = p.description?.trim();
23867
24031
  if (desc) for (const c of paramDocComment(desc)) fieldLines.push(` ${c}`);
23868
- if (p.required && !rust.startsWith("Option<")) {
24032
+ if (p.default != null) {
23869
24033
  if (desc) fieldLines.push(" ///");
24034
+ fieldLines.push(` /// Defaults to \`${formatDefault(p.default)}\`.`);
24035
+ }
24036
+ if (p.required && !rust.startsWith("Option<")) {
24037
+ if (desc || p.default != null) fieldLines.push(" ///");
23870
24038
  fieldLines.push(" /// Required.");
23871
24039
  }
23872
24040
  if (rust.startsWith("Option<")) fieldLines.push(" #[serde(skip_serializing_if = \"Option::is_none\")]");
24041
+ if (opts.isQuery && p.explode === false && isVecType(rust)) fieldLines.push(rust.startsWith("Option<") ? " #[serde(serialize_with = \"crate::query::serialize_comma_separated_opt\")]" : " #[serde(serialize_with = \"crate::query::serialize_comma_separated\")]");
23873
24042
  if (fname !== p.name) fieldLines.push(` #[serde(rename = ${JSON.stringify(p.name)})]`);
23874
24043
  if (p.deprecated) fieldLines.push(" #[deprecated]");
23875
24044
  fieldLines.push(` pub ${fname}: ${rust},`);
23876
24045
  fields.push({
23877
24046
  fname,
23878
24047
  rust,
23879
- required: !!p.required && !rust.startsWith("Option<")
24048
+ required: !!p.required && !rust.startsWith("Option<"),
24049
+ defaultExpr
23880
24050
  });
23881
24051
  };
23882
- for (const p of op.queryParams) emitField(p);
24052
+ for (const p of op.queryParams) emitField(p, { isQuery: true });
23883
24053
  for (const p of op.headerParams) emitField(p);
24054
+ for (const p of op.cookieParams ?? []) emitField(p);
24055
+ for (const g of queryGroupFields) {
24056
+ fieldLines.push(" #[serde(flatten)]");
24057
+ if (!g.required) fieldLines.push(" #[serde(skip_serializing_if = \"Option::is_none\")]");
24058
+ fieldLines.push(` pub ${g.name}: ${g.rustType},`);
24059
+ fields.push({
24060
+ fname: g.name,
24061
+ rust: g.rustType,
24062
+ required: g.required,
24063
+ defaultExpr: null
24064
+ });
24065
+ }
23884
24066
  if (op.requestBody) {
23885
- let bodyType = mapTypeRef$1(op.requestBody, {
24067
+ let bodyType;
24068
+ if (bodyGroupFields.length > 0) bodyType = registerSyntheticBody(op, name, bodyGroupParamNames, bodyGroupFields, ctx, registry, groupEmitter);
24069
+ else bodyType = mapTypeRef$1(op.requestBody, {
23886
24070
  hint: `${name}Body`,
23887
24071
  registry
23888
24072
  });
@@ -23895,21 +24079,36 @@ function renderParamsStruct(name, op, resolved, registry) {
23895
24079
  fields.push({
23896
24080
  fname: "body",
23897
24081
  rust: bodyType,
23898
- required: bodyRequired
24082
+ required: bodyRequired,
24083
+ defaultExpr: null
23899
24084
  });
23900
24085
  }
23901
24086
  const requiredFields = fields.filter((f) => f.required);
23902
- const derives = fields.length === 0 || requiredFields.length === 0 ? "Debug, Clone, Default, Serialize" : "Debug, Clone, Serialize";
24087
+ const allOptional = fields.length === 0 || requiredFields.length === 0;
24088
+ const hasSpecDefault = fields.some((f) => f.defaultExpr !== null);
24089
+ const derives = allOptional && !hasSpecDefault ? "Debug, Clone, Default, Serialize" : "Debug, Clone, Serialize";
23903
24090
  const out = [];
23904
24091
  if (fieldLines.length === 0) out.push(`#[derive(${derives})]`, `pub struct ${name} {}`);
23905
24092
  else out.push(`#[derive(${derives})]`, `pub struct ${name} {`, ...fieldLines, "}");
24093
+ if (allOptional && hasSpecDefault) {
24094
+ const defaultInitLines = fields.map((f) => ` ${f.fname}: ${f.defaultExpr ?? "Default::default()"},`);
24095
+ out.push("");
24096
+ out.push(`impl Default for ${name} {`);
24097
+ out.push(" #[allow(deprecated)]");
24098
+ out.push(" fn default() -> Self {");
24099
+ out.push(" Self {");
24100
+ out.push(...defaultInitLines);
24101
+ out.push(" }");
24102
+ out.push(" }");
24103
+ out.push("}");
24104
+ }
23906
24105
  if (requiredFields.length > 0) {
23907
24106
  const ctorArgs = requiredFields.map((f) => `${f.fname}: ${ctorParamType(f.rust)}`).join(", ");
23908
24107
  const initLines = [];
23909
24108
  for (const f of fields) if (f.required) {
23910
24109
  const value = ctorParamConvert(f.rust, f.fname);
23911
24110
  initLines.push(value === f.fname ? ` ${f.fname},` : ` ${f.fname}: ${value},`);
23912
- } else initLines.push(` ${f.fname}: Default::default(),`);
24111
+ } else initLines.push(` ${f.fname}: ${f.defaultExpr ?? "Default::default()"},`);
23913
24112
  out.push("");
23914
24113
  out.push(`impl ${name} {`);
23915
24114
  out.push(` /// Construct a new \`${name}\` with the required fields set.`);
@@ -23923,6 +24122,52 @@ function renderParamsStruct(name, op, resolved, registry) {
23923
24122
  }
23924
24123
  return out.join("\n");
23925
24124
  }
24125
+ /** True when a Rust type expression is a `Vec<…>` (or `Option<Vec<…>>`). */
24126
+ function isVecType(rust) {
24127
+ return (rust.startsWith("Option<") ? rust.slice(7, -1) : rust).startsWith("Vec<");
24128
+ }
24129
+ /**
24130
+ * Build a synthetic body struct for an op that has body-side parameter
24131
+ * groups. The original body model can't be reused as-is because its grouped
24132
+ * fields are still flat optionals; the synthetic type swaps them for a
24133
+ * flattened enum so callers commit to one variant at construction time.
24134
+ */
24135
+ function registerSyntheticBody(op, paramsName, bodyGroupParamNames, bodyGroupFields, ctx, registry, groupEmitter) {
24136
+ const bodyRef = op.requestBody;
24137
+ if (!bodyRef || bodyRef.kind !== "model") return mapTypeRef$1(bodyRef, {
24138
+ hint: `${paramsName}Body`,
24139
+ registry
24140
+ });
24141
+ const model = ctx.spec.models.find((m) => m.name === bodyRef.name);
24142
+ if (!model) return typeName(bodyRef.name);
24143
+ const name = `${paramsName}Body`;
24144
+ const flatFields = model.fields.filter((f) => !bodyGroupParamNames.has(f.name)).map((f) => {
24145
+ let rust = mapTypeRef$1(f.type, {
24146
+ hint: `${name}${typeName(f.name)}`,
24147
+ registry
24148
+ });
24149
+ if (!f.required && !rust.startsWith("Option<")) rust = makeOptional(rust);
24150
+ rust = applySecretRedaction(rust, f.name);
24151
+ return {
24152
+ rustName: fieldName(f.name),
24153
+ wireName: f.name,
24154
+ rustType: rust,
24155
+ required: !!f.required && !rust.startsWith("Option<"),
24156
+ doc: f.description
24157
+ };
24158
+ });
24159
+ const flattenEnums = bodyGroupFields.map((g) => ({
24160
+ rustName: g.name,
24161
+ rustType: g.rustType,
24162
+ required: g.required,
24163
+ doc: g.doc
24164
+ }));
24165
+ return groupEmitter.registerBody({
24166
+ name,
24167
+ flatFields,
24168
+ flattenEnums
24169
+ });
24170
+ }
23926
24171
  /** Constructor parameter type — accept `impl Into<String>` for ergonomic strings. */
23927
24172
  function ctorParamType(rust) {
23928
24173
  if (rust === "String") return "impl Into<String>";
@@ -23934,6 +24179,16 @@ function ctorParamConvert(rust, name) {
23934
24179
  if (rust === "crate::SecretString") return `${name}.into()`;
23935
24180
  return name;
23936
24181
  }
24182
+ /**
24183
+ * Detect a non-default per-operation security requirement (e.g. SSO's
24184
+ * `get_profile` requires an OAuth access token rather than the WorkOS API
24185
+ * key). Returns the snake_case parameter name to use for the override.
24186
+ */
24187
+ function bearerOverrideToken(op) {
24188
+ const override = op.security?.find((s) => s.schemeName !== "bearerAuth");
24189
+ if (!override) return null;
24190
+ return fieldName(override.schemeName);
24191
+ }
23937
24192
  function renderMethod(op, resolved, paramsType, method, emptyParams) {
23938
24193
  planOperation(op);
23939
24194
  const segments = parsePathTemplate(op.path);
@@ -23941,13 +24196,15 @@ function renderMethod(op, resolved, paramsType, method, emptyParams) {
23941
24196
  const pathArgNames = op.pathParams.map((p) => methodName(p.name));
23942
24197
  const returnType = renderResponseType(op);
23943
24198
  const bodyRequired = isBodyRequired(op);
24199
+ const tokenParam = bearerOverrideToken(op);
23944
24200
  const sig = [];
23945
24201
  for (const line of methodDocLines(op)) sig.push(` ${line}`);
23946
24202
  if (op.deprecated) sig.push(" #[deprecated]");
23947
24203
  const argsConvenience = [
23948
24204
  "&self",
23949
24205
  ...pathArgList,
23950
- ...emptyParams ? [] : [`params: ${paramsType}`]
24206
+ ...emptyParams ? [] : [`params: ${paramsType}`],
24207
+ ...tokenParam ? [`${tokenParam}: impl Into<String>`] : []
23951
24208
  ];
23952
24209
  const convenienceSig = ` pub async fn ${method}(${argsConvenience.join(", ")}) -> Result<${returnType}, Error> {`;
23953
24210
  if (convenienceSig.length <= 100) sig.push(convenienceSig);
@@ -23959,6 +24216,7 @@ function renderMethod(op, resolved, paramsType, method, emptyParams) {
23959
24216
  const delegateArgs = [
23960
24217
  ...pathArgNames,
23961
24218
  ...emptyParams ? [] : ["params"],
24219
+ ...tokenParam ? [tokenParam] : [],
23962
24220
  "None"
23963
24221
  ].join(", ");
23964
24222
  sig.push(` self.${method}_with_options(${delegateArgs}).await`);
@@ -23970,6 +24228,7 @@ function renderMethod(op, resolved, paramsType, method, emptyParams) {
23970
24228
  "&self",
23971
24229
  ...pathArgList,
23972
24230
  ...emptyParams ? [] : [`params: ${paramsType}`],
24231
+ ...tokenParam ? [`${tokenParam}: impl Into<String>`] : [],
23973
24232
  "options: Option<&crate::RequestOptions>"
23974
24233
  ];
23975
24234
  const optsSig = ` pub async fn ${method}_with_options(${argsOpts.join(", ")}) -> Result<${returnType}, Error> {`;
@@ -23988,6 +24247,14 @@ function renderMethod(op, resolved, paramsType, method, emptyParams) {
23988
24247
  sig.push(` let path = format!(${JSON.stringify(pathFormat)});`);
23989
24248
  } else sig.push(` let path = ${JSON.stringify(pathFormat)}.to_string();`);
23990
24249
  sig.push(` let method = http::Method::${op.httpMethod.toUpperCase()};`);
24250
+ if (tokenParam) {
24251
+ sig.push(` let ${tokenParam}: String = ${tokenParam}.into();`);
24252
+ sig.push(` let auth = http::HeaderValue::from_str(&format!("Bearer {${tokenParam}}"))`);
24253
+ sig.push(` .map_err(|e| Error::Builder(format!("invalid bearer token: {e}")))?;`);
24254
+ sig.push(" let mut merged = options.cloned().unwrap_or_default();");
24255
+ sig.push(" merged.extra_headers.push((http::header::AUTHORIZATION, auth));");
24256
+ sig.push(" let options = Some(&merged);");
24257
+ }
23991
24258
  const queryRef = emptyParams ? "&()" : "&params";
23992
24259
  const emptyResp = isEmptyResponse(op);
23993
24260
  const bodyMethod = emptyResp ? "request_with_body_opts_empty" : "request_with_body_opts";
@@ -24006,28 +24273,95 @@ function renderMethod(op, resolved, paramsType, method, emptyParams) {
24006
24273
  return sig.join("\n");
24007
24274
  }
24008
24275
  /**
24009
- * Generate a `<method>_auto_paging` helper for paginated list endpoints.
24010
- * Returns null when the operation isn't a recognised list endpoint (response
24011
- * model lacks both `data: Vec<T>` and `list_metadata`, or the params struct
24012
- * has no `after` cursor).
24276
+ * Render a URL-builder method. URL-builder ops (e.g. `GET /sso/authorize`,
24277
+ * `GET /sso/logout`) issue no HTTP request — they format a redirect URL the
24278
+ * application sends the user to. Generated methods return `Result<String,
24279
+ * Error>` because percent-encoding the query string can still fail.
24280
+ */
24281
+ function renderUrlBuilderMethod(op, resolved, paramsType, method, emptyParams) {
24282
+ const segments = parsePathTemplate(op.path);
24283
+ const pathArgList = op.pathParams.map((p) => `${methodName(p.name)}: &str`);
24284
+ op.pathParams.map((p) => methodName(p.name));
24285
+ const sig = [];
24286
+ for (const line of methodDocLines(op)) sig.push(` ${line}`);
24287
+ if (op.deprecated) sig.push(" #[deprecated]");
24288
+ const args = [
24289
+ "&self",
24290
+ ...pathArgList,
24291
+ ...emptyParams ? [] : [`params: ${paramsType}`]
24292
+ ];
24293
+ const headSig = ` pub fn ${method}(${args.join(", ")}) -> Result<String, Error> {`;
24294
+ if (headSig.length <= 100) sig.push(headSig);
24295
+ else {
24296
+ sig.push(` pub fn ${method}(`);
24297
+ for (const arg of args) sig.push(` ${arg},`);
24298
+ sig.push(" ) -> Result<String, Error> {");
24299
+ }
24300
+ const pathFormat = segments.map((s) => s.kind === "literal" ? s.value : `{${methodName(s.name)}}`).join("");
24301
+ if (segments.some((s) => s.kind === "param")) {
24302
+ for (const p of op.pathParams) {
24303
+ const n = methodName(p.name);
24304
+ sig.push(` let ${n} = crate::client::path_segment(${n});`);
24305
+ }
24306
+ sig.push(` let path = format!(${JSON.stringify(pathFormat)});`);
24307
+ } else sig.push(` let path = ${JSON.stringify(pathFormat)}.to_string();`);
24308
+ const defaults = resolved.defaults ?? {};
24309
+ const inferred = resolved.inferFromClient ?? [];
24310
+ if (Object.keys(defaults).length > 0 || inferred.length > 0) {
24311
+ sig.push(" let mut overlay = serde_json::Map::new();");
24312
+ for (const [k, v] of Object.entries(defaults)) sig.push(` overlay.insert(${JSON.stringify(k)}.to_string(), serde_json::json!(${JSON.stringify(v)}));`);
24313
+ for (const k of inferred) sig.push(` overlay.insert(${JSON.stringify(k)}.to_string(), serde_json::Value::String(${clientFieldExpression(k)}.to_string()));`);
24314
+ if (!emptyParams) {
24315
+ sig.push(" let params_value = serde_json::to_value(&params)");
24316
+ sig.push(" .map_err(|e| Error::Builder(format!(\"query encode failed: {e}\")))?;");
24317
+ sig.push(" if let serde_json::Value::Object(map) = params_value {");
24318
+ sig.push(" for (k, v) in map { overlay.insert(k, v); }");
24319
+ sig.push(" }");
24320
+ }
24321
+ sig.push(" let merged = serde_json::Value::Object(overlay);");
24322
+ sig.push(" let qs = crate::query::encode_query(&merged)?;");
24323
+ } else if (!emptyParams) sig.push(" let qs = crate::query::encode_query(&params)?;");
24324
+ else sig.push(" let qs = String::new();");
24325
+ sig.push(" let url = if qs.is_empty() {");
24326
+ sig.push(" format!(\"{}{}\", self.client.base_url(), path)");
24327
+ sig.push(" } else {");
24328
+ sig.push(" format!(\"{}{}?{}\", self.client.base_url(), path, qs)");
24329
+ sig.push(" };");
24330
+ sig.push(" Ok(url)");
24331
+ sig.push(" }");
24332
+ return sig.join("\n");
24333
+ }
24334
+ /**
24335
+ * Generate a `<method>_auto_paging` helper from `op.pagination`. Returns null
24336
+ * when the operation isn't paginated, when the strategy isn't `cursor`, or
24337
+ * when the response model lacks the expected `data` / pagination-cursor
24338
+ * fields (defensive — the IR shouldn't produce that combination today).
24013
24339
  */
24014
- function renderAutoPagingMethod(op, _resolved, paramsType, method, ctx) {
24015
- if (!op.response || op.response.kind !== "model") return null;
24016
- const responseRef = op.response;
24017
- const responseModel = ctx.spec.models.find((m) => m.name === responseRef.name);
24340
+ function renderAutoPagingMethod(op, resolved, paramsType, method, ctx) {
24341
+ if (!op.pagination) return null;
24342
+ if (op.pagination.strategy !== "cursor") return null;
24343
+ if (resolved.urlBuilder) return null;
24344
+ if (op.response.kind !== "model") return null;
24345
+ const responseModel = ctx.spec.models.find((m) => m.name === op.response.name);
24018
24346
  if (!responseModel) return null;
24019
- const dataField = responseModel.fields.find((f) => f.name === "data");
24020
- const hasListMetadata = responseModel.fields.some((f) => f.name === "list_metadata");
24021
- if (!dataField || !hasListMetadata) return null;
24022
- if (dataField.type.kind !== "array") return null;
24347
+ const cursorParam = op.pagination.param;
24348
+ const dataPath = op.pagination.dataPath ?? "data";
24349
+ const dataField = responseModel.fields.find((f) => f.name === dataPath);
24350
+ if (!dataField || dataField.type.kind !== "array") return null;
24351
+ const listMetadataField = responseModel.fields.find((f) => f.name === "list_metadata");
24352
+ if (!listMetadataField || listMetadataField.type.kind !== "model") return null;
24353
+ const metadataModel = ctx.spec.models.find((m) => m.name === listMetadataField.type.name);
24354
+ if (!metadataModel) return null;
24355
+ if (!metadataModel.fields.some((f) => f.name === cursorParam)) return null;
24023
24356
  const itemType = mapTypeRef$1(dataField.type.items);
24024
- if (!op.queryParams.some((p) => p.name === "after")) return null;
24357
+ const cursorField = fieldName(cursorParam);
24358
+ const dataAccessor = fieldName(dataPath);
24025
24359
  const pathArgList = op.pathParams.map((p) => `${methodName(p.name)}: impl Into<String>`);
24026
24360
  const pathArgNames = op.pathParams.map((p) => methodName(p.name));
24027
24361
  const sig = [];
24028
24362
  sig.push("");
24029
24363
  sig.push(` /// Returns an async [\`futures_util::Stream\`] that yields every \`${itemType}\``);
24030
- sig.push(` /// across all pages, advancing the \`after\` cursor under the hood.`);
24364
+ sig.push(` /// across all pages, advancing the \`${cursorParam}\` cursor under the hood.`);
24031
24365
  sig.push(" ///");
24032
24366
  sig.push(" /// ```ignore");
24033
24367
  sig.push(" /// use futures_util::TryStreamExt;");
@@ -24053,17 +24387,17 @@ function renderAutoPagingMethod(op, _resolved, paramsType, method, ctx) {
24053
24387
  sig.push(" crate::pagination::auto_paginate_pages(move |after| {");
24054
24388
  for (const n of pathArgNames) sig.push(` let ${n} = ${n}.clone();`);
24055
24389
  sig.push(" let mut params = params.clone();");
24056
- sig.push(" params.after = after;");
24390
+ sig.push(` params.${cursorField} = after;`);
24057
24391
  sig.push(" async move {");
24058
24392
  const callArgs = [...pathArgNames.map((n) => `&${n}`), "params"].join(", ");
24059
24393
  sig.push(` let page = self.${method}(${callArgs}).await?;`);
24060
- sig.push(" Ok((page.data, page.list_metadata.after))");
24394
+ sig.push(` Ok((page.${dataAccessor}, page.list_metadata.${cursorField}))`);
24061
24395
  sig.push(" }");
24062
24396
  sig.push(" })");
24063
24397
  sig.push(" }");
24064
24398
  return sig.join("\n");
24065
24399
  }
24066
- function renderWrapperParamsStruct(name, _op, _wrapper, params, registry) {
24400
+ function renderWrapperParamsStruct(name, _op, _wrapper, params, registry, ctx) {
24067
24401
  const fields = [];
24068
24402
  const fieldLines = [];
24069
24403
  const seen = /* @__PURE__ */ new Set();
@@ -24081,32 +24415,52 @@ function renderWrapperParamsStruct(name, _op, _wrapper, params, registry) {
24081
24415
  rust = applySecretRedaction(rust, rp.paramName);
24082
24416
  const desc = rp.field?.description?.trim();
24083
24417
  if (desc) for (const c of paramDocComment(desc)) fieldLines.push(` ${c}`);
24418
+ const fieldDefault = rp.field?.default;
24419
+ if (fieldDefault != null) {
24420
+ if (desc) fieldLines.push(" ///");
24421
+ fieldLines.push(` /// Defaults to \`${formatDefault(fieldDefault)}\`.`);
24422
+ }
24084
24423
  const required = !rp.isOptional && !rust.startsWith("Option<");
24085
24424
  if (required) {
24086
- if (desc) fieldLines.push(" ///");
24425
+ if (desc || fieldDefault != null) fieldLines.push(" ///");
24087
24426
  fieldLines.push(" /// Required.");
24088
24427
  }
24089
24428
  if (rust.startsWith("Option<")) fieldLines.push(" #[serde(skip_serializing_if = \"Option::is_none\")]");
24090
24429
  if (fname !== rp.paramName) fieldLines.push(` #[serde(rename = ${JSON.stringify(rp.paramName)})]`);
24091
24430
  fieldLines.push(` pub ${fname}: ${rust},`);
24431
+ const defaultExpr = fieldDefault != null && rp.field ? rustDefaultExpr(fieldDefault, rp.field.type, rust.startsWith("Option<"), ctx) : null;
24092
24432
  fields.push({
24093
24433
  fname,
24094
24434
  rust,
24095
- required
24435
+ required,
24436
+ defaultExpr
24096
24437
  });
24097
24438
  }
24098
24439
  const requiredFields = fields.filter((f) => f.required);
24099
- const derives = fields.length === 0 || requiredFields.length === 0 ? "Debug, Clone, Default, Serialize" : "Debug, Clone, Serialize";
24440
+ const allOptional = fields.length === 0 || requiredFields.length === 0;
24441
+ const hasSpecDefault = fields.some((f) => f.defaultExpr !== null);
24442
+ const derives = allOptional && !hasSpecDefault ? "Debug, Clone, Default, Serialize" : "Debug, Clone, Serialize";
24100
24443
  const out = [];
24101
24444
  if (fieldLines.length === 0) out.push(`#[derive(${derives})]`, `pub struct ${name} {}`);
24102
24445
  else out.push(`#[derive(${derives})]`, `pub struct ${name} {`, ...fieldLines, "}");
24446
+ if (allOptional && hasSpecDefault) {
24447
+ const defaultInitLines = fields.map((f) => ` ${f.fname}: ${f.defaultExpr ?? "Default::default()"},`);
24448
+ out.push("");
24449
+ out.push(`impl Default for ${name} {`);
24450
+ out.push(" fn default() -> Self {");
24451
+ out.push(" Self {");
24452
+ out.push(...defaultInitLines);
24453
+ out.push(" }");
24454
+ out.push(" }");
24455
+ out.push("}");
24456
+ }
24103
24457
  if (requiredFields.length > 0) {
24104
24458
  const ctorArgs = requiredFields.map((f) => `${f.fname}: ${ctorParamType(f.rust)}`).join(", ");
24105
24459
  const initLines = [];
24106
24460
  for (const f of fields) if (f.required) {
24107
24461
  const value = ctorParamConvert(f.rust, f.fname);
24108
24462
  initLines.push(value === f.fname ? ` ${f.fname},` : ` ${f.fname}: ${value},`);
24109
- } else initLines.push(` ${f.fname}: Default::default(),`);
24463
+ } else initLines.push(` ${f.fname}: ${f.defaultExpr ?? "Default::default()"},`);
24110
24464
  out.push("");
24111
24465
  out.push(`impl ${name} {`);
24112
24466
  out.push(` /// Construct a new \`${name}\` with the required fields set.`);
@@ -24207,6 +24561,47 @@ function clientFieldExpression(field) {
24207
24561
  function paramDocComment(text) {
24208
24562
  return text.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((l) => `/// ${l}`);
24209
24563
  }
24564
+ /**
24565
+ * Render a spec-level default value for inclusion in a doc comment. Strings
24566
+ * render bare (e.g. `desc`) so they nest naturally inside the surrounding
24567
+ * backticks; numbers/booleans use JSON encoding.
24568
+ */
24569
+ function formatDefault(value) {
24570
+ if (typeof value === "string") return value;
24571
+ return JSON.stringify(value);
24572
+ }
24573
+ /**
24574
+ * Render a spec-level default value as a Rust expression suitable for an
24575
+ * `impl Default` body or a `new(…)` initialiser. When `isOptional` is true the
24576
+ * result is wrapped in `Some(…)` so it matches an `Option<T>` field.
24577
+ *
24578
+ * Returns `null` for types/values the emitter doesn't know how to materialise
24579
+ * — caller falls back to `Default::default()`.
24580
+ */
24581
+ function rustDefaultExpr(value, ref, isOptional, ctx) {
24582
+ if (ref.kind === "nullable") return rustDefaultExpr(value, ref.inner, isOptional, ctx);
24583
+ let expr = null;
24584
+ switch (ref.kind) {
24585
+ case "primitive":
24586
+ if (ref.type === "integer" && typeof value === "number" && Number.isFinite(value)) expr = String(Math.trunc(value));
24587
+ else if (ref.type === "number" && typeof value === "number" && Number.isFinite(value)) expr = Number.isInteger(value) ? `${value}.0` : String(value);
24588
+ else if (ref.type === "boolean" && typeof value === "boolean") expr = String(value);
24589
+ else if (ref.type === "string" && typeof value === "string") expr = `${JSON.stringify(value)}.to_string()`;
24590
+ break;
24591
+ case "enum": {
24592
+ if (typeof value !== "string" && typeof value !== "number") break;
24593
+ const enumDef = ctx.spec.enums.find((e) => e.name === ref.name);
24594
+ if (!enumDef) break;
24595
+ const ev = enumDef.values.find((v) => v.value === value);
24596
+ if (!ev) break;
24597
+ expr = `${typeName(ref.name)}::${variantName(ev.value)}`;
24598
+ break;
24599
+ }
24600
+ default: break;
24601
+ }
24602
+ if (expr === null) return null;
24603
+ return isOptional ? `Some(${expr})` : expr;
24604
+ }
24210
24605
  function methodDocLines(op) {
24211
24606
  const lines = [];
24212
24607
  if (op.description && op.description.trim().length > 0) for (const raw of op.description.split("\n")) {
@@ -24240,16 +24635,19 @@ function isBodyRequired(op) {
24240
24635
  }
24241
24636
  /**
24242
24637
  * `true` when the resolved operation contributes nothing to a params struct:
24243
- * no request body, and every exposed query/header parameter is inferred from
24244
- * the client or supplied as a default. Such methods take no `params:` arg in
24245
- * the public API and skip the empty struct entirely.
24638
+ * no request body, and every exposed query/header/cookie parameter is
24639
+ * inferred from the client or supplied as a default. Such methods take no
24640
+ * `params:` arg in the public API and skip the empty struct entirely.
24246
24641
  */
24247
24642
  function isEmptyParams(op, resolved) {
24248
24643
  if (op.requestBody) return false;
24249
24644
  const hidden = new Set([...Object.keys(resolved.defaults ?? {}), ...resolved.inferFromClient ?? []]);
24250
- const visibleQuery = op.queryParams.filter((p) => !hidden.has(p.name));
24645
+ const grouped = /* @__PURE__ */ new Set();
24646
+ for (const g of op.parameterGroups ?? []) for (const v of g.variants) for (const p of v.parameters) grouped.add(p.name);
24647
+ const visibleQuery = op.queryParams.filter((p) => !hidden.has(p.name) && !grouped.has(p.name));
24251
24648
  const visibleHeader = op.headerParams.filter((p) => !hidden.has(p.name));
24252
- return visibleQuery.length === 0 && visibleHeader.length === 0;
24649
+ const visibleCookie = (op.cookieParams ?? []).filter((p) => !hidden.has(p.name));
24650
+ return visibleQuery.length === 0 && visibleHeader.length === 0 && visibleCookie.length === 0 && (op.parameterGroups?.length ?? 0) === 0;
24253
24651
  }
24254
24652
  function renderResourcesBarrel(exports) {
24255
24653
  const seen = /* @__PURE__ */ new Set();
@@ -24356,7 +24754,8 @@ function generateModelFixture(model, modelMap, enumMap, visiting) {
24356
24754
  visiting.add(model.name);
24357
24755
  for (const field of model.fields) {
24358
24756
  if (!field.required) continue;
24359
- result[field.name] = exampleFor(field.type, modelMap, enumMap, visiting, field.name);
24757
+ const fromExample = exampleFromSpec(field.example, field.type, enumMap);
24758
+ result[field.name] = fromExample !== void 0 ? fromExample : exampleFor(field.type, modelMap, enumMap, visiting, field.name);
24360
24759
  }
24361
24760
  visiting.delete(model.name);
24362
24761
  return result;
@@ -24398,13 +24797,80 @@ function exampleFor(type, modelMap, enumMap, visiting, fieldName) {
24398
24797
  }
24399
24798
  }
24400
24799
  }
24800
+ /**
24801
+ * Resolve a spec-provided `example` against a TypeRef and return the value to
24802
+ * embed in the fixture, or `undefined` when the example cannot be used safely.
24803
+ *
24804
+ * "Safely" means the value would round-trip through serde to the generated
24805
+ * Rust type. We deliberately only accept primitives, enum string/number
24806
+ * values, and homogenous arrays of those; nested object examples (which the
24807
+ * spec sometimes supplies as illustrative metadata blobs) are skipped because
24808
+ * they rarely match the strict struct shape Rust expects.
24809
+ */
24810
+ function exampleFromSpec(example, type, enumMap) {
24811
+ if (example === void 0) return void 0;
24812
+ if (example === null) return void 0;
24813
+ return matchExampleToType(example, type, enumMap);
24814
+ }
24815
+ function matchExampleToType(value, type, enumMap) {
24816
+ switch (type.kind) {
24817
+ case "primitive": return matchPrimitive(value, type.type);
24818
+ case "literal": return value === type.value ? value : void 0;
24819
+ case "enum": {
24820
+ const e = enumMap.get(type.name);
24821
+ if (!e) return void 0;
24822
+ return e.values.some((v) => v.value === value) ? value : void 0;
24823
+ }
24824
+ case "array": {
24825
+ if (!Array.isArray(value)) return void 0;
24826
+ const out = [];
24827
+ for (const item of value) {
24828
+ const matched = matchExampleToType(item, type.items, enumMap);
24829
+ if (matched === void 0) return void 0;
24830
+ out.push(matched);
24831
+ }
24832
+ if (out.length === 0) return void 0;
24833
+ return out;
24834
+ }
24835
+ case "nullable": return matchExampleToType(value, type.inner, enumMap);
24836
+ case "map":
24837
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
24838
+ return value;
24839
+ case "union":
24840
+ for (const variant of type.variants) {
24841
+ const matched = matchExampleToType(value, variant, enumMap);
24842
+ if (matched !== void 0) return matched;
24843
+ }
24844
+ return;
24845
+ case "model": return;
24846
+ }
24847
+ }
24848
+ function matchPrimitive(value, primitive) {
24849
+ switch (primitive) {
24850
+ case "string": return typeof value === "string" ? value : void 0;
24851
+ case "integer": return typeof value === "number" && Number.isInteger(value) ? value : void 0;
24852
+ case "number": return typeof value === "number" ? value : void 0;
24853
+ case "boolean": return typeof value === "boolean" ? value : void 0;
24854
+ case "unknown": return value;
24855
+ }
24856
+ }
24401
24857
  //#endregion
24402
24858
  //#region src/rust/tests.ts
24403
24859
  /**
24404
24860
  * Generate integration tests under `tests/`. Each mount group gets one
24405
- * `tests/{mount}_test.rs` file. Generated tests construct params, mock the
24406
- * expected request, then call the SDK method and assert the request was sent
24407
- * (`Mock::expect(1)`). JSON fixtures are emitted alongside.
24861
+ * `tests/{mount}_test.rs` file. Per operation the generator emits:
24862
+ *
24863
+ * - `_round_trip`: happy-path 200 mock + call.
24864
+ * - `_unauthorized`, `_not_found`, `_rate_limited`, `_server_error`: error
24865
+ * paths for every HTTP-calling op.
24866
+ * - `_bad_request`, `_unprocessable`: additional 4xx error paths for write
24867
+ * ops (POST/PUT/PATCH/DELETE).
24868
+ * - `_empty_page`: empty `data: []` response for paginated ops.
24869
+ * - `_encodes_query_params`: outbound query-string assertion for ops with
24870
+ * array query params (`explode: true` repeated keys, `explode: false`
24871
+ * comma-joined).
24872
+ *
24873
+ * URL-builder ops (no HTTP call) only get the round-trip test.
24408
24874
  */
24409
24875
  function generateTests(spec, ctx) {
24410
24876
  const files = [];
@@ -24456,6 +24922,7 @@ function renderMountTest(mountName, resolvedOps, ctx, modelMap, enumMap) {
24456
24922
  lines.push("");
24457
24923
  lines.push("use wiremock::matchers::{method, path as path_matcher};");
24458
24924
  lines.push("use wiremock::{Mock, MockServer, ResponseTemplate};");
24925
+ lines.push(`use ${crate}::Error;`);
24459
24926
  lines.push("");
24460
24927
  const seen = /* @__PURE__ */ new Set();
24461
24928
  for (const r of resolvedOps) {
@@ -24485,43 +24952,316 @@ function renderRegularTest(op, resolved, accessor, crate, modelMap, enumMap) {
24485
24952
  const literalPath = op.path.replace(/\{[^}]+\}/g, "test_id");
24486
24953
  const httpMethod = op.httpMethod.toUpperCase();
24487
24954
  const responseExpr = responseBodyExpr(op.response, modelMap, enumMap);
24955
+ const isUrlBuilder = resolved.urlBuilder === true;
24956
+ const callArgs = buildCallArgs(op, resolved, crate, accessor, modelMap, enumMap).join(", ");
24957
+ const shape = {
24958
+ methodIdent: m,
24959
+ literalPath,
24960
+ httpMethod,
24961
+ callArgs,
24962
+ isUrlBuilder,
24963
+ isWrite: isWriteMethod(op.httpMethod),
24964
+ isPaginated: !!op.pagination,
24965
+ hasBodyGroup: !!op.requestBody && hasBodyParameterGroup(op)
24966
+ };
24967
+ const lines = [];
24968
+ lines.push("#[tokio::test]");
24969
+ lines.push(`async fn ${accessor}_${m}_round_trip() {`);
24970
+ if (isUrlBuilder) {
24971
+ lines.push(" let server = MockServer::start().await;");
24972
+ lines.push(" let client = common::test_client(&server).await;");
24973
+ lines.push(` let _ = client.${accessor}().${m}(${callArgs});`);
24974
+ } else {
24975
+ lines.push(" let server = MockServer::start().await;");
24976
+ lines.push(` Mock::given(method(${JSON.stringify(httpMethod)}))`);
24977
+ lines.push(` .and(path_matcher(${JSON.stringify(literalPath)}))`);
24978
+ lines.push(` .respond_with(ResponseTemplate::new(200).set_body_string(${responseExpr}))`);
24979
+ lines.push(" .expect(1)");
24980
+ lines.push(" .mount(&server)");
24981
+ lines.push(" .await;");
24982
+ lines.push(" let client = common::test_client(&server).await;");
24983
+ lines.push(` let _ = client.${accessor}().${m}(${callArgs}).await;`);
24984
+ }
24985
+ lines.push("}");
24986
+ if (isUrlBuilder) return lines;
24987
+ for (const errTest of standardErrorTests(shape, accessor)) {
24988
+ lines.push("");
24989
+ lines.push(...errTest);
24990
+ }
24991
+ if (shape.isWrite) for (const errTest of writeErrorTests(shape, accessor)) {
24992
+ lines.push("");
24993
+ lines.push(...errTest);
24994
+ }
24995
+ if (shape.isPaginated) {
24996
+ const emptyTest = emptyPageTest(op, shape, accessor);
24997
+ if (emptyTest) {
24998
+ lines.push("");
24999
+ lines.push(...emptyTest);
25000
+ }
25001
+ }
25002
+ const encodingTest = encodesQueryParamsTest(op, resolved, accessor, crate, modelMap, enumMap);
25003
+ if (encodingTest) {
25004
+ lines.push("");
25005
+ lines.push(...encodingTest);
25006
+ }
25007
+ return lines;
25008
+ }
25009
+ /**
25010
+ * Build the positional args for the resource method call: path-param literals,
25011
+ * then a single params struct (when the op has any), then a token-string for
25012
+ * non-bearer security overrides. This factors the constructor logic out of
25013
+ * the round-trip renderer so the error/encoding tests can reuse it verbatim
25014
+ * — every test category sends the same request, only the mocked response and
25015
+ * assertion differ.
25016
+ */
25017
+ function buildCallArgs(op, resolved, crate, accessor, modelMap, enumMap) {
24488
25018
  const hidden = new Set([...Object.keys(resolved.defaults ?? {}), ...resolved.inferFromClient ?? []]);
24489
- const visibleQuery = op.queryParams.filter((p) => !hidden.has(p.name));
25019
+ const groupedNames = /* @__PURE__ */ new Set();
25020
+ for (const g of op.parameterGroups ?? []) for (const v of g.variants) for (const p of v.parameters) groupedNames.add(p.name);
25021
+ const visibleQuery = op.queryParams.filter((p) => !hidden.has(p.name) && !groupedNames.has(p.name));
24490
25022
  const visibleHeader = op.headerParams.filter((p) => !hidden.has(p.name));
24491
25023
  const visibleParams = [...visibleQuery, ...visibleHeader];
24492
25024
  const requiredParams = visibleParams.filter((p) => p.required);
24493
25025
  const hasBody = op.requestBody !== void 0;
24494
25026
  const bodyRequired = hasBody && op.requestBody.kind !== "nullable";
24495
- const emptyParams = !hasBody && visibleParams.length === 0;
25027
+ const queryNames = new Set(op.queryParams.map((p) => p.name));
25028
+ const requiredQueryGroups = (op.parameterGroups ?? []).filter((g) => !g.optional).filter((g) => g.variants.every((v) => v.parameters.every((p) => queryNames.has(p.name))));
25029
+ const emptyParams = !hasBody && visibleParams.length === 0 && (op.parameterGroups?.length ?? 0) === 0;
24496
25030
  const callArgs = [];
24497
25031
  for (const _ of op.pathParams) callArgs.push("\"test_id\"");
24498
25032
  if (!emptyParams) {
24499
25033
  const paramsType = `${crate}::${accessor}::${typeName(resolved.methodName)}Params`;
24500
- if (requiredParams.length === 0 && !bodyRequired) callArgs.push(`${paramsType}::default()`);
25034
+ if (requiredParams.length === 0 && !bodyRequired && requiredQueryGroups.length === 0) callArgs.push(`${paramsType}::default()`);
24501
25035
  else {
24502
25036
  const ctorArgs = [];
24503
25037
  for (const p of requiredParams) ctorArgs.push(stubExpr(p.type, p.name, modelMap, enumMap));
24504
- if (hasBody) {
24505
- if (bodyRequired) ctorArgs.push(stubExpr(op.requestBody, "body", modelMap, enumMap));
24506
- }
25038
+ for (const g of requiredQueryGroups) ctorArgs.push(parameterGroupStubExpr(g, crate, accessor));
25039
+ if (hasBody && bodyRequired) if (hasBodyParameterGroup(op)) ctorArgs.push(syntheticBodyStubExpr(op, resolved, crate, accessor, modelMap));
25040
+ else ctorArgs.push(stubExpr(op.requestBody, "body", modelMap, enumMap));
24507
25041
  callArgs.push(`${paramsType}::new(${ctorArgs.join(", ")})`);
24508
25042
  }
24509
25043
  }
25044
+ const tokenName = bearerOverrideTokenName(op);
25045
+ if (tokenName) callArgs.push(`"stub_${tokenName}".to_string()`);
25046
+ return callArgs;
25047
+ }
25048
+ /**
25049
+ * Build the four common-error test bodies (401/404/429/500). Each one mocks a
25050
+ * single response with the given status, calls the SDK method (which should
25051
+ * fail), and asserts on the unwrapped `Error::Api` payload.
25052
+ */
25053
+ function standardErrorTests(shape, accessor) {
25054
+ const tests = [];
25055
+ tests.push(errorTestBody(shape, accessor, "unauthorized", 401, "{\"message\":\"Unauthorized\"}", void 0));
25056
+ tests.push(errorTestBody(shape, accessor, "not_found", 404, "{\"message\":\"Not found\"}", void 0));
25057
+ tests.push(errorTestBody(shape, accessor, "rate_limited", 429, "{\"message\":\"Slow down\"}", {
25058
+ retryAfterSeconds: 1,
25059
+ assertRetryAfter: true
25060
+ }));
25061
+ tests.push(errorTestBody(shape, accessor, "server_error", 500, "{\"message\":\"Internal error\"}", void 0));
25062
+ return tests;
25063
+ }
25064
+ /** Build the two write-op-only error tests (400/422). */
25065
+ function writeErrorTests(shape, accessor) {
25066
+ return [errorTestBody(shape, accessor, "bad_request", 400, "{\"code\":\"validation_error\",\"message\":\"Bad request\"}", { assertCode: "validation_error" }), errorTestBody(shape, accessor, "unprocessable", 422, "{\"message\":\"Unprocessable\"}", void 0)];
25067
+ }
25068
+ function errorTestBody(shape, accessor, category, status, body, opts) {
24510
25069
  const lines = [];
24511
25070
  lines.push("#[tokio::test]");
24512
- lines.push(`async fn ${accessor}_${m}_round_trip() {`);
25071
+ lines.push(`async fn ${accessor}_${shape.methodIdent}_${category}() {`);
24513
25072
  lines.push(" let server = MockServer::start().await;");
24514
- lines.push(` Mock::given(method(${JSON.stringify(httpMethod)}))`);
24515
- lines.push(` .and(path_matcher(${JSON.stringify(literalPath)}))`);
24516
- lines.push(` .respond_with(ResponseTemplate::new(200).set_body_string(${responseExpr}))`);
25073
+ lines.push(` let template = ResponseTemplate::new(${status})`);
25074
+ if (opts?.retryAfterSeconds !== void 0) lines.push(` .insert_header("retry-after", ${JSON.stringify(String(opts.retryAfterSeconds))})`);
25075
+ lines.push(` .set_body_string(${JSON.stringify(body)});`);
25076
+ lines.push(` Mock::given(method(${JSON.stringify(shape.httpMethod)}))`);
25077
+ lines.push(` .and(path_matcher(${JSON.stringify(shape.literalPath)}))`);
25078
+ lines.push(" .respond_with(template)");
24517
25079
  lines.push(" .expect(1)");
24518
25080
  lines.push(" .mount(&server)");
24519
25081
  lines.push(" .await;");
24520
25082
  lines.push(" let client = common::test_client(&server).await;");
24521
- lines.push(` let _ = client.${accessor}().${m}(${callArgs.join(", ")}).await;`);
25083
+ lines.push(` let err = client.${accessor}().${shape.methodIdent}(${shape.callArgs}).await.expect_err("expected error");`);
25084
+ lines.push(" let api = match &err {");
25085
+ lines.push(" Error::Api(api) => api.as_ref(),");
25086
+ lines.push(" other => panic!(\"expected Error::Api, got {other:?}\"),");
25087
+ lines.push(" };");
25088
+ lines.push(` assert_eq!(api.status, ${status});`);
25089
+ if (opts?.assertRetryAfter) lines.push(` assert_eq!(api.retry_after, Some(std::time::Duration::from_secs(${opts.retryAfterSeconds ?? 0})));`);
25090
+ if (opts?.assertCode) lines.push(` assert_eq!(api.code.as_deref(), Some(${JSON.stringify(opts.assertCode)}));`);
24522
25091
  lines.push("}");
24523
25092
  return lines;
24524
25093
  }
25094
+ /**
25095
+ * Return an `_empty_page` test for a paginated list op. Two shapes are
25096
+ * supported:
25097
+ *
25098
+ * - Wrapper model: `{"data": [], "list_metadata": {...}}`, accessed via
25099
+ * `resp.data` on the returned struct.
25100
+ * - Bare array: `[]`, accessed via `resp.is_empty()` directly (the SDK
25101
+ * returns `Vec<T>` for paginated ops without a wrapper model).
25102
+ *
25103
+ * Returns null when the response shape isn't recognised (e.g. a primitive
25104
+ * or unknown shape we can't safely assert against).
25105
+ */
25106
+ function emptyPageTest(op, shape, accessor) {
25107
+ const responseKind = op.response.kind;
25108
+ let body;
25109
+ let dataAccessor;
25110
+ if (responseKind === "array") {
25111
+ body = "[]";
25112
+ dataAccessor = "resp";
25113
+ } else if (responseKind === "model") {
25114
+ body = "{\"object\":\"list\",\"data\":[],\"list_metadata\":{\"before\":null,\"after\":null}}";
25115
+ dataAccessor = "resp.data";
25116
+ } else return null;
25117
+ const lines = [];
25118
+ lines.push("#[tokio::test]");
25119
+ lines.push(`async fn ${accessor}_${shape.methodIdent}_empty_page() {`);
25120
+ lines.push(" let server = MockServer::start().await;");
25121
+ lines.push(` Mock::given(method(${JSON.stringify(shape.httpMethod)}))`);
25122
+ lines.push(` .and(path_matcher(${JSON.stringify(shape.literalPath)}))`);
25123
+ lines.push(` .respond_with(ResponseTemplate::new(200).set_body_string(${JSON.stringify(body)}))`);
25124
+ lines.push(" .expect(1)");
25125
+ lines.push(" .mount(&server)");
25126
+ lines.push(" .await;");
25127
+ lines.push(" let client = common::test_client(&server).await;");
25128
+ lines.push(` let resp = client.${accessor}().${shape.methodIdent}(${shape.callArgs}).await.expect("expected success");`);
25129
+ lines.push(` assert!(${dataAccessor}.is_empty(), "expected empty data array");`);
25130
+ lines.push("}");
25131
+ return lines;
25132
+ }
25133
+ /**
25134
+ * Build an `_encodes_query_params` test when the op declares at least one
25135
+ * array query param. Constructs a params struct with a known Vec value on
25136
+ * each array field and asserts the actual outbound query string contains
25137
+ * either repeated keys (`events=foo&events=bar`, default for `explode: true`)
25138
+ * or a comma-joined value (`events=foo%2Cbar`, when `explode: false`).
25139
+ *
25140
+ * Returns null when no array query params apply — those ops have nothing
25141
+ * interesting to encode.
25142
+ */
25143
+ function encodesQueryParamsTest(op, resolved, accessor, crate, modelMap, enumMap) {
25144
+ const hidden = new Set([...Object.keys(resolved.defaults ?? {}), ...resolved.inferFromClient ?? []]);
25145
+ const groupedNames = /* @__PURE__ */ new Set();
25146
+ for (const g of op.parameterGroups ?? []) for (const v of g.variants) for (const p of v.parameters) groupedNames.add(p.name);
25147
+ const arrayParams = op.queryParams.filter((p) => !hidden.has(p.name) && !groupedNames.has(p.name) && isStringArrayParam(p));
25148
+ if (arrayParams.length === 0) return null;
25149
+ const target = arrayParams[0];
25150
+ const expectedFragment = target.explode !== false ? `${target.name}=foo&${target.name}=bar` : `${target.name}=foo%2Cbar`;
25151
+ const callArgs = buildCallArgs(op, resolved, crate, accessor, modelMap, enumMap);
25152
+ const pathArgCount = op.pathParams.length;
25153
+ const tokenName = bearerOverrideTokenName(op);
25154
+ if (!(callArgs.length > pathArgCount && (tokenName ? callArgs.length > pathArgCount + 1 : true))) return null;
25155
+ const tokenArg = tokenName ? callArgs[callArgs.length - 1] : null;
25156
+ const paramsExpr = callArgs[pathArgCount];
25157
+ const lines = [];
25158
+ const respExpr = encodingResponseExpr(op, modelMap, enumMap);
25159
+ lines.push("#[tokio::test]");
25160
+ lines.push(`async fn ${accessor}_${methodName(resolved.methodName)}_encodes_query_params() {`);
25161
+ lines.push(" let server = MockServer::start().await;");
25162
+ lines.push(` Mock::given(method(${JSON.stringify("GET".replace("GET", op.httpMethod.toUpperCase()))}))`);
25163
+ lines.push(` .and(path_matcher(${JSON.stringify(op.path.replace(/\{[^}]+\}/g, "test_id"))}))`);
25164
+ lines.push(` .respond_with(ResponseTemplate::new(200).set_body_string(${respExpr}))`);
25165
+ lines.push(" .mount(&server)");
25166
+ lines.push(" .await;");
25167
+ lines.push(" let client = common::test_client(&server).await;");
25168
+ if (paramsExpr.endsWith("::default()")) {
25169
+ const ty = paramsExpr.slice(0, -11);
25170
+ lines.push(` let params = ${ty} {`);
25171
+ lines.push(` ${fieldIdent(target.name)}: Some(vec!["foo".to_string(), "bar".to_string()]),`);
25172
+ lines.push(" ..Default::default()");
25173
+ lines.push(" };");
25174
+ } else {
25175
+ lines.push(` let mut params = ${paramsExpr};`);
25176
+ lines.push(` params.${fieldIdent(target.name)} = Some(vec!["foo".to_string(), "bar".to_string()]);`);
25177
+ }
25178
+ const passArgs = [];
25179
+ for (let i = 0; i < pathArgCount; i++) passArgs.push(callArgs[i]);
25180
+ passArgs.push("params");
25181
+ if (tokenArg) passArgs.push(tokenArg);
25182
+ lines.push(` let _ = client.${accessor}().${methodName(resolved.methodName)}(${passArgs.join(", ")}).await;`);
25183
+ lines.push(" let received = server.received_requests().await.expect(\"recorded requests\");");
25184
+ lines.push(" let request = received.first().expect(\"at least one request\");");
25185
+ lines.push(" let query = request.url.query().unwrap_or(\"\");");
25186
+ lines.push(` assert!(query.contains(${JSON.stringify(expectedFragment)}), "expected query to contain {:?}, got {:?}", ${JSON.stringify(expectedFragment)}, query);`);
25187
+ lines.push("}");
25188
+ return lines;
25189
+ }
25190
+ /** Body expression for the encoding-test response (success, ignored). */
25191
+ function encodingResponseExpr(op, modelMap, enumMap) {
25192
+ if (op.pagination) {
25193
+ if (op.response.kind === "array") return JSON.stringify("[]");
25194
+ return JSON.stringify("{\"object\":\"list\",\"data\":[],\"list_metadata\":{\"before\":null,\"after\":null}}");
25195
+ }
25196
+ return responseBodyExpr(op.response, modelMap, enumMap);
25197
+ }
25198
+ /**
25199
+ * True if `param.type` is `Vec<String>` (or `Option<Vec<String>>`). Restricts
25200
+ * the encoding test to string arrays because non-string arrays would need
25201
+ * per-type constructors we can't synthesise reliably.
25202
+ */
25203
+ function isStringArrayParam(p) {
25204
+ let t = p.type;
25205
+ while (t.kind === "nullable") t = t.inner;
25206
+ if (t.kind !== "array") return false;
25207
+ let inner = t.items;
25208
+ while (inner.kind === "nullable") inner = inner.inner;
25209
+ return inner.kind === "primitive" && inner.type === "string";
25210
+ }
25211
+ /** Snake-case field accessor matching the resources emitter's naming. */
25212
+ function fieldIdent(name) {
25213
+ return methodName(name);
25214
+ }
25215
+ /** True for HTTP methods that mutate state and should retry-defensively. */
25216
+ function isWriteMethod(method) {
25217
+ return method !== "get" && method !== "head";
25218
+ }
25219
+ /** True when the op has at least one body-side parameter group. */
25220
+ function hasBodyParameterGroup(op) {
25221
+ const queryNames = new Set(op.queryParams.map((p) => p.name));
25222
+ return (op.parameterGroups ?? []).some((g) => g.variants.every((v) => v.parameters.every((p) => !queryNames.has(p.name))));
25223
+ }
25224
+ /** Pick the snake_case token-arg name for an op with a non-bearer security override. */
25225
+ function bearerOverrideTokenName(op) {
25226
+ const override = op.security?.find((s) => s.schemeName !== "bearerAuth");
25227
+ if (!override) return null;
25228
+ return override.schemeName.replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`).replace(/^_/, "");
25229
+ }
25230
+ /**
25231
+ * Construct a synthetic body type via its `new(...)` constructor. The
25232
+ * resources emitter passes required flat fields and required flatten enums
25233
+ * positionally; mirror that ordering here so the stub compiles against the
25234
+ * generated `impl <Type> { fn new(...) }`.
25235
+ */
25236
+ function syntheticBodyStubExpr(op, resolved, crate, accessor, modelMap) {
25237
+ const bodyRef = op.requestBody;
25238
+ const fqn = `${crate}::${accessor}::${`${typeName(resolved.methodName)}ParamsBody`}`;
25239
+ const model = bodyRef.kind === "model" ? modelMap.get(bodyRef.name) ?? null : null;
25240
+ const queryNames = new Set(op.queryParams.map((p) => p.name));
25241
+ const bodyGroupNames = /* @__PURE__ */ new Set();
25242
+ for (const g of op.parameterGroups ?? []) if (g.variants.every((v) => v.parameters.every((p) => !queryNames.has(p.name)))) for (const v of g.variants) for (const p of v.parameters) bodyGroupNames.add(p.name);
25243
+ const args = [];
25244
+ if (model) for (const f of model.fields) {
25245
+ if (bodyGroupNames.has(f.name)) continue;
25246
+ if (!(!!f.required && f.type.kind !== "nullable")) continue;
25247
+ args.push(`${JSON.stringify(`stub_${f.name}`)}.to_string()`);
25248
+ }
25249
+ for (const g of op.parameterGroups ?? []) {
25250
+ if (!g.variants.every((v) => v.parameters.every((p) => !queryNames.has(p.name)))) continue;
25251
+ if (g.optional) continue;
25252
+ args.push(parameterGroupStubExpr(g, crate, accessor));
25253
+ }
25254
+ return `${fqn}::new(${args.join(", ")})`;
25255
+ }
25256
+ /** First-variant stub for a parameter-group enum, fully crate-qualified. */
25257
+ function parameterGroupStubExpr(group, crate, accessor) {
25258
+ const enumName = group.name.split("_").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
25259
+ const firstVariant = group.variants[0];
25260
+ const variantName = firstVariant.name.split("_").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
25261
+ const fqn = `${crate}::${accessor}::${enumName}`;
25262
+ if (firstVariant.parameters.length === 0) return `${fqn}::${variantName}`;
25263
+ return `${fqn}::${variantName} { ${firstVariant.parameters.map((p) => `${p.name}: ${JSON.stringify(`stub_${p.name}`)}.to_string()`).join(", ")} }`;
25264
+ }
24525
25265
  /** Test for a wrapper-method operation. */
24526
25266
  function renderWrapperTest(op, wrapper, ctx, accessor, crate, modelMap, enumMap) {
24527
25267
  const m = methodName(wrapper.name);
@@ -24544,6 +25284,7 @@ function renderWrapperTest(op, wrapper, ctx, accessor, crate, modelMap, enumMap)
24544
25284
  });
24545
25285
  callArgs.push(`${paramsType}::new(${ctorArgs.join(", ")})`);
24546
25286
  }
25287
+ const callArgsStr = callArgs.join(", ");
24547
25288
  const lines = [];
24548
25289
  lines.push("#[tokio::test]");
24549
25290
  lines.push(`async fn ${accessor}_${m}_round_trip() {`);
@@ -24555,8 +25296,26 @@ function renderWrapperTest(op, wrapper, ctx, accessor, crate, modelMap, enumMap)
24555
25296
  lines.push(" .mount(&server)");
24556
25297
  lines.push(" .await;");
24557
25298
  lines.push(" let client = common::test_client(&server).await;");
24558
- lines.push(` let _ = client.${accessor}().${m}(${callArgs.join(", ")}).await;`);
25299
+ lines.push(` let _ = client.${accessor}().${m}(${callArgsStr}).await;`);
24559
25300
  lines.push("}");
25301
+ const shape = {
25302
+ methodIdent: m,
25303
+ literalPath,
25304
+ httpMethod,
25305
+ callArgs: callArgsStr,
25306
+ isUrlBuilder: false,
25307
+ isWrite: isWriteMethod(op.httpMethod),
25308
+ isPaginated: !!op.pagination,
25309
+ hasBodyGroup: false
25310
+ };
25311
+ for (const errTest of standardErrorTests(shape, accessor)) {
25312
+ lines.push("");
25313
+ lines.push(...errTest);
25314
+ }
25315
+ if (shape.isWrite) for (const errTest of writeErrorTests(shape, accessor)) {
25316
+ lines.push("");
25317
+ lines.push(...errTest);
25318
+ }
24560
25319
  return lines;
24561
25320
  }
24562
25321
  /** Rust string-expression for the mock response body. */
@@ -24727,4 +25486,4 @@ const workosEmittersPlugin = {
24727
25486
  //#endregion
24728
25487
  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 };
24729
25488
 
24730
- //# sourceMappingURL=plugin-C408Wh-o.mjs.map
25489
+ //# sourceMappingURL=plugin-CmfzawTp.mjs.map