@workos/oagen-emitters 0.8.1 → 0.8.2

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.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.8.1"
2
+ ".": "0.8.2"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.2](https://github.com/workos/oagen-emitters/compare/v0.8.1...v0.8.2) (2026-05-05)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **kotlin:** improve emitter output quality ([#86](https://github.com/workos/oagen-emitters/issues/86)) ([6dd9b2a](https://github.com/workos/oagen-emitters/commit/6dd9b2ad6904ebb088cba65801d4987a2af61482))
9
+
3
10
  ## [0.8.1](https://github.com/workos/oagen-emitters/compare/v0.8.0...v0.8.1) (2026-05-05)
4
11
 
5
12
 
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as nodeEmitter, a as rustExtractor, c as pythonExtractor, d as rubyEmitter, f as kotlinEmitter, g as pythonEmitter, h as phpEmitter, i as kotlinExtractor, l as rubyExtractor, m as goEmitter, n as elixirExtractor, o as goExtractor, p as dotnetEmitter, r as dotnetExtractor, s as phpExtractor, t as workosEmittersPlugin, u as nodeExtractor } from "./plugin-DOE0FqrZ.mjs";
1
+ import { _ as nodeEmitter, a as rustExtractor, c as pythonExtractor, d as rubyEmitter, f as kotlinEmitter, g as pythonEmitter, h as phpEmitter, i as kotlinExtractor, l as rubyExtractor, m as goEmitter, n as elixirExtractor, o as goExtractor, p as dotnetEmitter, r as dotnetExtractor, s as phpExtractor, t as workosEmittersPlugin, u as nodeExtractor } from "./plugin-CeNME04k.mjs";
2
2
  export { dotnetEmitter, dotnetExtractor, elixirExtractor, goEmitter, goExtractor, kotlinEmitter, kotlinExtractor, nodeEmitter, nodeExtractor, phpEmitter, phpExtractor, pythonEmitter, pythonExtractor, rubyEmitter, rubyExtractor, rustExtractor, workosEmittersPlugin };
@@ -17749,6 +17749,40 @@ const KOTLIN_SRC_PREFIX$2 = "src/main/kotlin/";
17749
17749
  const MODELS_PACKAGE = "com.workos.models";
17750
17750
  const MODELS_DIR = "com/workos/models";
17751
17751
  /**
17752
+ * Some specs leave string fields without `format: date-time` even though the
17753
+ * description (or the example) makes clear they carry an ISO-8601 timestamp.
17754
+ * Detect that here so we can promote the type to `OffsetDateTime` in the
17755
+ * Kotlin output.
17756
+ */
17757
+ const ISO_8601_DESCRIPTION_RE$2 = /\bISO[-_ ]?8601\b/i;
17758
+ function looksLikeIso8601String$2(description) {
17759
+ if (!description) return false;
17760
+ return ISO_8601_DESCRIPTION_RE$2.test(description);
17761
+ }
17762
+ function promoteIso8601TypeRef$2(type, description) {
17763
+ if (!looksLikeIso8601String$2(description)) return type;
17764
+ const promote = (t) => {
17765
+ if (t.kind === "primitive" && t.type === "string" && !t.format) return {
17766
+ kind: "primitive",
17767
+ type: "string",
17768
+ format: "date-time"
17769
+ };
17770
+ if (t.kind === "nullable") return {
17771
+ kind: "nullable",
17772
+ inner: promote(t.inner)
17773
+ };
17774
+ return t;
17775
+ };
17776
+ return promote(type);
17777
+ }
17778
+ function promoteFieldType$1(f) {
17779
+ const promoted = promoteIso8601TypeRef$2(f.type, f.description);
17780
+ return promoted === f.type ? f : {
17781
+ ...f,
17782
+ type: promoted
17783
+ };
17784
+ }
17785
+ /**
17752
17786
  * Generate Kotlin `data class` models. Each model becomes a separate `.kt`
17753
17787
  * file under `com.workos.models`. Discriminated unions emit a sealed class
17754
17788
  * with Jackson `@JsonTypeInfo` / `@JsonSubTypes` annotations so the base type
@@ -17985,7 +18019,8 @@ function emitWorkOSEvent(eventMapping) {
17985
18019
  function renderFields(fields, overrideFields = /* @__PURE__ */ new Set()) {
17986
18020
  const seen = /* @__PURE__ */ new Set();
17987
18021
  const lines = [];
17988
- for (const field of fields) {
18022
+ for (const rawField of fields) {
18023
+ const field = promoteFieldType$1(rawField);
17989
18024
  const kotlinName = propertyName(field.name);
17990
18025
  if (seen.has(kotlinName)) continue;
17991
18026
  seen.add(kotlinName);
@@ -18004,7 +18039,7 @@ function renderFields(fields, overrideFields = /* @__PURE__ */ new Set()) {
18004
18039
  const isOverride = overrideFields.has(kotlinName);
18005
18040
  const annotations = [];
18006
18041
  annotations.push(`@JsonProperty(${ktStringLiteral(field.name)})`);
18007
- if (field.deprecated) annotations.push("@Deprecated(\"Deprecated field\")");
18042
+ if (field.deprecated) annotations.push(buildDeprecatedAnnotation(field.description));
18008
18043
  const paramParts = [];
18009
18044
  if (field.description?.trim()) {
18010
18045
  const line = field.description.split("\n").find((l) => l.trim()) ?? "";
@@ -18035,6 +18070,26 @@ function collapseFieldEntries(rawLines) {
18035
18070
  return entries;
18036
18071
  }
18037
18072
  /**
18073
+ * Pull the most useful free-form deprecation hint out of a field description
18074
+ * and lift it into the `@Deprecated(...)` message argument. Most WorkOS
18075
+ * deprecations are written as a description that begins with "Deprecated"
18076
+ * (e.g. "Deprecated. Use `domain_data` instead."). When the description
18077
+ * doesn't carry a hint we fall back to a short, self-explanatory message
18078
+ * rather than the generic "Deprecated field" placeholder.
18079
+ */
18080
+ function deprecationMessageFromDescription(description) {
18081
+ if (!description) return "Deprecated.";
18082
+ const firstLine = description.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
18083
+ if (!firstLine) return "Deprecated.";
18084
+ const collapsed = firstLine.replace(/\s+/g, " ").trim();
18085
+ if (collapsed.length === 0) return "Deprecated.";
18086
+ if (/\bdeprecat/i.test(collapsed)) return collapsed;
18087
+ return "Deprecated.";
18088
+ }
18089
+ function buildDeprecatedAnnotation(description) {
18090
+ return `@Deprecated(${ktStringLiteral(deprecationMessageFromDescription(description))})`;
18091
+ }
18092
+ /**
18038
18093
  * If the TypeRef is a literal (const) with a string, number, or boolean value,
18039
18094
  * return the Kotlin expression for that default. Otherwise return null.
18040
18095
  */
@@ -18049,7 +18104,8 @@ function collectImports(fields) {
18049
18104
  const imports = /* @__PURE__ */ new Set();
18050
18105
  if (fields.length === 0) return imports;
18051
18106
  imports.add("com.fasterxml.jackson.annotation.JsonProperty");
18052
- for (const field of fields) {
18107
+ for (const rawField of fields) {
18108
+ const field = promoteFieldType$1(rawField);
18053
18109
  const mapped = mapTypeRef$1(field.type);
18054
18110
  if (/\bOffsetDateTime\b/.test(mapped)) imports.add("java.time.OffsetDateTime");
18055
18111
  for (const enumName of collectEnumNames(field.type)) {
@@ -18206,21 +18262,21 @@ function emitWrapperMethod$1(resolvedOp, wrapper, ctx) {
18206
18262
  if (opDesc) kdocLines.push(opDesc.split("\n")[0]);
18207
18263
  else kdocLines.push(`${wrapperHumanName.charAt(0).toUpperCase()}${wrapperHumanName.slice(1)}.`);
18208
18264
  const paramDocs = [];
18209
- for (const pp of pathParams) if (pp.description?.trim()) paramDocs.push(`@param ${propertyName(pp.name)} ${escapeKdoc$1(pp.description.split("\n")[0].trim())}`);
18210
- for (const rp of resolvedParams) {
18211
- const desc = rp.field?.description?.trim();
18212
- if (desc) paramDocs.push(`@param ${propertyName(rp.paramName)} ${escapeKdoc$1(desc.split("\n")[0])}`);
18213
- }
18265
+ const pushParamDoc = (kotlinName, sourceName, description) => {
18266
+ const firstLine = description?.split("\n").find((l) => l.trim())?.trim() ?? "";
18267
+ const fallback = `the ${humanize(sourceName)} of the request.`;
18268
+ const text = firstLine || fallback;
18269
+ paramDocs.push(`@param ${kotlinName} ${escapeKdoc$1(text)}`);
18270
+ };
18271
+ for (const pp of pathParams) pushParamDoc(propertyName(pp.name), pp.name, pp.description);
18272
+ for (const rp of resolvedParams) pushParamDoc(propertyName(rp.paramName), rp.paramName, rp.field?.description);
18273
+ pushParamDoc("requestOptions", "request_options", "per-request overrides (idempotency key, API key, headers, timeout)");
18214
18274
  if (responseClass) paramDocs.push(`@return the ${responseClass}`);
18215
- if (paramDocs.length > 0 || kdocLines.length > 0) {
18216
- lines.push(" /**");
18217
- for (const l of kdocLines) lines.push(` * ${escapeKdoc$1(l)}`);
18218
- if (paramDocs.length > 0) {
18219
- lines.push(" *");
18220
- for (const p of paramDocs) lines.push(` * ${p}`);
18221
- }
18222
- lines.push(" */");
18223
- }
18275
+ lines.push(" /**");
18276
+ for (const l of kdocLines) lines.push(` * ${escapeKdoc$1(l)}`);
18277
+ lines.push(" *");
18278
+ for (const p of paramDocs) lines.push(` * ${p}`);
18279
+ lines.push(" */");
18224
18280
  lines.push(" @JvmOverloads");
18225
18281
  const params = [];
18226
18282
  for (const pp of pathParams) params.push(` ${propertyName(pp.name)}: String`);
@@ -18243,6 +18299,25 @@ function emitWrapperMethod$1(resolvedOp, wrapper, ctx) {
18243
18299
  }
18244
18300
  lines.push(` )${returnClause} {`);
18245
18301
  }
18302
+ const inferred = wrapper.inferFromClient ?? [];
18303
+ const usesStandardClientCreds = inferred.includes("client_id") && inferred.includes("client_secret");
18304
+ if (op.path === "/user_management/authenticate" && op.httpMethod.toUpperCase() === "POST" && responseClass === "AuthenticateResponse" && typeof wrapper.defaults?.grant_type === "string" && usesStandardClientCreds) {
18305
+ const grantType = wrapper.defaults.grant_type;
18306
+ lines.push(` return authenticate(`);
18307
+ lines.push(` grantType = ${ktLiteral(grantType)},`);
18308
+ lines.push(` requestOptions = requestOptions,`);
18309
+ const entryLines = resolvedParams.map((rp) => {
18310
+ const paramName = propertyName(rp.paramName);
18311
+ return ` ${ktLiteral(rp.paramName)} to ${paramName}`;
18312
+ });
18313
+ for (let i = 0; i < entryLines.length; i++) {
18314
+ const sep = i === entryLines.length - 1 ? "" : ",";
18315
+ lines.push(`${entryLines[i]}${sep}`);
18316
+ }
18317
+ lines.push(` )`);
18318
+ lines.push(" }");
18319
+ return lines;
18320
+ }
18246
18321
  const bodyEntries = [];
18247
18322
  for (const rp of resolvedParams) {
18248
18323
  const paramName = propertyName(rp.paramName);
@@ -18301,6 +18376,52 @@ function isHandwrittenOverride(op) {
18301
18376
  //#region src/kotlin/resources.ts
18302
18377
  const KOTLIN_SRC_PREFIX$1 = "src/main/kotlin/";
18303
18378
  /**
18379
+ * Some specs leave query params / fields typed as plain `string` even though
18380
+ * the description (or the field name) makes clear they carry an ISO-8601
18381
+ * timestamp. Detecting that here lets us emit `OffsetDateTime` so callers
18382
+ * don't have to format the wire string themselves.
18383
+ */
18384
+ const ISO_8601_DESCRIPTION_RE$1 = /\bISO[-_ ]?8601\b/i;
18385
+ function looksLikeIso8601String$1(description) {
18386
+ if (!description) return false;
18387
+ return ISO_8601_DESCRIPTION_RE$1.test(description);
18388
+ }
18389
+ /**
18390
+ * Promote a string `TypeRef` to a `format: date-time` primitive when the
18391
+ * accompanying description identifies it as an ISO-8601 timestamp. Leaves
18392
+ * non-string types untouched.
18393
+ */
18394
+ function promoteIso8601TypeRef$1(type, description) {
18395
+ if (!looksLikeIso8601String$1(description)) return type;
18396
+ const promote = (t) => {
18397
+ if (t.kind === "primitive" && t.type === "string" && !t.format) return {
18398
+ kind: "primitive",
18399
+ type: "string",
18400
+ format: "date-time"
18401
+ };
18402
+ if (t.kind === "nullable") return {
18403
+ kind: "nullable",
18404
+ inner: promote(t.inner)
18405
+ };
18406
+ return t;
18407
+ };
18408
+ return promote(type);
18409
+ }
18410
+ function promoteParameterType(p) {
18411
+ const promoted = promoteIso8601TypeRef$1(p.type, p.description);
18412
+ return promoted === p.type ? p : {
18413
+ ...p,
18414
+ type: promoted
18415
+ };
18416
+ }
18417
+ function promoteFieldType(f) {
18418
+ const promoted = promoteIso8601TypeRef$1(f.type, f.description);
18419
+ return promoted === f.type ? f : {
18420
+ ...f,
18421
+ type: promoted
18422
+ };
18423
+ }
18424
+ /**
18304
18425
  * Generate one API class per mount group. Methods map 1:1 to IR operations.
18305
18426
  * Path params, query params, and body fields are flattened into the method
18306
18427
  * signature so callers never need to construct an intermediate options object.
@@ -18431,9 +18552,9 @@ function renderMethod(_mountName, method, op, ctx, resolvedOp, imports) {
18431
18552
  const pathParams = sortPathParamsByTemplateOrder(op);
18432
18553
  const groupedParamNames = collectGroupedParamNames(op);
18433
18554
  const hasGroups = (op.parameterGroups?.length ?? 0) > 0;
18434
- const queryParams = op.queryParams.filter((p) => !hidden.has(p.name) && !groupedParamNames.has(p.name));
18555
+ const queryParams = op.queryParams.filter((p) => !hidden.has(p.name) && !groupedParamNames.has(p.name)).map(promoteParameterType);
18435
18556
  const bodyModel = resolveBodyModel$2(op, ctx);
18436
- const bodyFields = bodyModel ? bodyModel.fields.filter((f) => !hidden.has(f.name) && !groupedParamNames.has(f.name)) : [];
18557
+ const bodyFields = bodyModel ? bodyModel.fields.filter((f) => !hidden.has(f.name) && !groupedParamNames.has(f.name)).map(promoteFieldType) : [];
18437
18558
  for (const p of [...pathParams, ...queryParams]) registerTypeImports(p.type, imports, ctx);
18438
18559
  for (const f of bodyFields) registerTypeImports(f.type, imports, ctx);
18439
18560
  const paginatedItemName = resolvePaginatedItemName(plan.paginatedItemModelName, ctx);
@@ -18544,9 +18665,8 @@ function renderMethod(_mountName, method, op, ctx, resolvedOp, imports) {
18544
18665
  lines.push(` before = ${pickNamedQueryParam(sortedQuery, "before")},`);
18545
18666
  lines.push(` after = ${pickNamedQueryParam(sortedQuery, "after")}`);
18546
18667
  lines.push(` ) {`);
18547
- lines.push(` val params = this`);
18548
- for (const qp of sortedQuery.filter((p) => p.name !== "after" && p.name !== "before")) for (const ln of emitQueryParam(qp, " ")) lines.push(ln);
18549
- for (const group of op.parameterGroups ?? []) for (const ln of emitGroupQueryDispatch(group, groupParamNames.get(group.name), " ")) lines.push(ln);
18668
+ for (const qp of sortedQuery.filter((p) => p.name !== "after" && p.name !== "before")) for (const ln of emitQueryParam(qp, " ", true)) lines.push(ln);
18669
+ for (const group of op.parameterGroups ?? []) for (const ln of emitGroupQueryDispatch(group, groupParamNames.get(group.name), " ", true)) lines.push(ln);
18550
18670
  lines.push(` }`);
18551
18671
  } else {
18552
18672
  const groupsGoToQuery = hasGroups && !hasBody;
@@ -18666,14 +18786,15 @@ function buildMethodKdoc(op, pathParams, queryParams, bodyFields, bodyParamNames
18666
18786
  if (descriptionRaw) for (const l of descriptionRaw.split("\n")) textLines.push(escapeKdoc(l));
18667
18787
  const paramDocs = [];
18668
18788
  const seenParamDocs = /* @__PURE__ */ new Set();
18669
- const pushParamDoc = (name, description, deprecated) => {
18789
+ const pushParamDoc = (name, sourceName, description, deprecated) => {
18670
18790
  if (seenParamDocs.has(name)) return;
18671
18791
  seenParamDocs.add(name);
18672
- paramDocs.push(formatParamDoc(name, description, deprecated));
18792
+ paramDocs.push(formatParamDoc(name, description, deprecated, sourceName));
18673
18793
  };
18674
- for (const pp of pathParams) if (pp.description?.trim() || pp.deprecated) pushParamDoc(propertyName(pp.name), pp.description, pp.deprecated);
18675
- for (const qp of queryParams) if (qp.description?.trim() || qp.deprecated) pushParamDoc(propertyName(qp.name), qp.description, qp.deprecated);
18676
- for (const bf of bodyFields) if (bf.description?.trim() || bf.deprecated) pushParamDoc(bodyParamNames.get(bf.name), bf.description, bf.deprecated);
18794
+ for (const pp of pathParams) pushParamDoc(propertyName(pp.name), pp.name, pp.description, pp.deprecated);
18795
+ for (const qp of queryParams) pushParamDoc(propertyName(qp.name), qp.name, qp.description, qp.deprecated);
18796
+ for (const bf of bodyFields) pushParamDoc(bodyParamNames.get(bf.name), bf.name, bf.description, bf.deprecated);
18797
+ pushParamDoc("requestOptions", "request_options", REQUEST_OPTIONS_PARAM_DESCRIPTION);
18677
18798
  const returnDoc = plan.isPaginated ? "@return a [com.workos.common.http.Page] of results" : plan.responseModelName ? `@return the ${plan.isArrayResponse ? `list of ${className$1(plan.responseModelName)}` : className$1(plan.responseModelName)}` : null;
18678
18799
  if (!(textLines.length > 0 || paramDocs.length > 0 || returnDoc !== null)) return [];
18679
18800
  const out = [" /**"];
@@ -18687,10 +18808,17 @@ function buildMethodKdoc(op, pathParams, queryParams, bodyFields, bodyParamNames
18687
18808
  out.push(" */");
18688
18809
  return out;
18689
18810
  }
18690
- function formatParamDoc(kotlinName, description, deprecated) {
18691
- const text = (description?.split("\n").find((l) => l.trim()) ?? "").trim();
18692
- const parts = [deprecated ? "**Deprecated.**" : "", text].filter(Boolean).join(" ");
18693
- return `@param ${kotlinName}${parts ? ` ${escapeKdoc(parts)}` : ""}`;
18811
+ /**
18812
+ * Stable, canned description for the trailing `requestOptions` parameter that
18813
+ * every generated method exposes. Kept as a constant so the same phrasing
18814
+ * appears across resource methods, wrapper methods, and union-split helpers.
18815
+ */
18816
+ const REQUEST_OPTIONS_PARAM_DESCRIPTION = "per-request overrides (idempotency key, API key, headers, timeout)";
18817
+ function formatParamDoc(kotlinName, description, deprecated, sourceName) {
18818
+ const specText = (description?.split("\n").find((l) => l.trim()) ?? "").trim();
18819
+ const deprecationNote = deprecated ? "**Deprecated.**" : "";
18820
+ const fallback = `the ${humanize(sourceName ?? kotlinName)} of the request.`;
18821
+ return `@param ${kotlinName} ${escapeKdoc([deprecationNote, specText || fallback].filter(Boolean).join(" "))}`;
18694
18822
  }
18695
18823
  /**
18696
18824
  * Unwrap a possibly-nullable type to check if the inner type is an array,
@@ -18709,32 +18837,43 @@ function unwrapArray(t) {
18709
18837
  function valueExprForQuery(type) {
18710
18838
  const inner = type.kind === "nullable" ? type.inner : type;
18711
18839
  if (inner.kind === "enum") return "it.value";
18712
- if (inner.kind === "primitive" && inner.type === "string") return "it";
18840
+ if (inner.kind === "primitive" && inner.type === "string") return inner.format === "date-time" ? "it.toString()" : "it";
18713
18841
  return "it.toString()";
18714
18842
  }
18715
- function emitQueryParam(p, indent) {
18843
+ function emitQueryParam(p, indent, receiverMode = false) {
18716
18844
  const prop = propertyName(p.name);
18717
18845
  const rendered = queryParamToString(p.type, prop);
18718
18846
  const inner = p.type.kind === "nullable" ? p.type.inner : p.type;
18719
18847
  const arrayItem = unwrapArray(p.type);
18848
+ const callPrefix = receiverMode ? "" : "params.";
18849
+ const addPair = (pair) => receiverMode ? `add(${pair})` : `params += ${pair}`;
18720
18850
  if (arrayItem) {
18721
18851
  const explode = p.explode ?? true;
18722
18852
  const itemExpr = valueExprForQuery(arrayItem);
18853
+ const isIdentity = itemExpr === "it";
18723
18854
  if (!explode) {
18724
- if (p.required) return [`${indent}params.addJoinedIfNotNull(${ktLiteral(p.name)}, ${prop}.map { ${itemExpr} })`];
18725
- return [`${indent}params.addJoinedIfNotNull(${ktLiteral(p.name)}, ${prop}?.map { ${itemExpr} })`];
18855
+ if (p.required) {
18856
+ const arg = isIdentity ? prop : `${prop}.map { ${itemExpr} }`;
18857
+ return [`${indent}${callPrefix}addJoinedIfNotNull(${ktLiteral(p.name)}, ${arg})`];
18858
+ }
18859
+ const arg = isIdentity ? prop : `${prop}?.map { ${itemExpr} }`;
18860
+ return [`${indent}${callPrefix}addJoinedIfNotNull(${ktLiteral(p.name)}, ${arg})`];
18726
18861
  }
18727
- if (p.required) return [`${indent}params.addEach(${ktLiteral(p.name)}, ${prop}.map { ${itemExpr} })`];
18728
- return [`${indent}${prop}?.let { params.addEach(${ktLiteral(p.name)}, it.map { ${itemExpr} }) }`];
18862
+ if (p.required) {
18863
+ const arg = isIdentity ? prop : `${prop}.map { ${itemExpr} }`;
18864
+ return [`${indent}${callPrefix}addEach(${ktLiteral(p.name)}, ${arg})`];
18865
+ }
18866
+ if (isIdentity) return [`${indent}${prop}?.let { ${callPrefix}addEach(${ktLiteral(p.name)}, it) }`];
18867
+ return [`${indent}${prop}?.let { ${callPrefix}addEach(${ktLiteral(p.name)}, it.map { ${itemExpr} }) }`];
18729
18868
  }
18730
- if (p.required) return [`${indent}params += ${ktLiteral(p.name)} to ${rendered}`];
18731
- if (inner.kind === "primitive" && inner.type === "string") return [`${indent}params.addIfNotNull(${ktLiteral(p.name)}, ${prop})`];
18732
- return [`${indent}${prop}?.let { params += ${ktLiteral(p.name)} to ${queryParamToString(inner, "it")} }`];
18869
+ if (p.required) return [`${indent}${addPair(`${ktLiteral(p.name)} to ${rendered}`)}`];
18870
+ if (inner.kind === "primitive" && inner.type === "string" && inner.format !== "date-time") return [`${indent}${callPrefix}addIfNotNull(${ktLiteral(p.name)}, ${prop})`];
18871
+ return [`${indent}${prop}?.let { ${addPair(`${ktLiteral(p.name)} to ${queryParamToString(inner, "it")}`)} }`];
18733
18872
  }
18734
18873
  function queryParamToString(type, varName) {
18735
18874
  if (type.kind === "enum") return `${varName}.value`;
18736
18875
  if (type.kind === "nullable") return queryParamToString(type.inner, varName);
18737
- if (type.kind === "primitive" && type.type === "string") return varName;
18876
+ if (type.kind === "primitive" && type.type === "string") return type.format === "date-time" ? `${varName}.toString()` : varName;
18738
18877
  return `${varName}.toString()`;
18739
18878
  }
18740
18879
  function pickNamedQueryParam(sorted, name) {
@@ -18848,14 +18987,14 @@ function generateSealedClass(group, bodyFieldTypes) {
18848
18987
  return lines;
18849
18988
  }
18850
18989
  /** Emit `when` dispatch that serializes a parameter group into query params. */
18851
- function emitGroupQueryDispatch(group, prop, indent) {
18990
+ function emitGroupQueryDispatch(group, prop, indent, receiverMode = false) {
18852
18991
  const sealedName = sealedGroupName$1(group.name);
18853
18992
  const lines = [];
18854
18993
  if (group.optional) {
18855
18994
  lines.push(`${indent}if (${prop} != null) {`);
18856
- emitWhenBlock(lines, group, sealedName, prop, `${indent} `);
18995
+ emitWhenBlock(lines, group, sealedName, prop, `${indent} `, receiverMode);
18857
18996
  lines.push(`${indent}}`);
18858
- } else emitWhenBlock(lines, group, sealedName, prop, indent);
18997
+ } else emitWhenBlock(lines, group, sealedName, prop, indent, receiverMode);
18859
18998
  return lines;
18860
18999
  }
18861
19000
  function assignGroupParameterNames$1(op, occupiedNames) {
@@ -18882,13 +19021,14 @@ function reserveUniqueGroupParameterName$1(base, occupiedNames) {
18882
19021
  occupiedNames.add(fallback);
18883
19022
  return fallback;
18884
19023
  }
18885
- function emitWhenBlock(lines, group, sealedName, prop, indent) {
19024
+ function emitWhenBlock(lines, group, sealedName, prop, indent, receiverMode = false) {
18886
19025
  lines.push(`${indent}when (${prop}) {`);
18887
19026
  for (const variant of group.variants) {
18888
19027
  const variantName = className$1(variant.name);
18889
19028
  const entries = variant.parameters.map((p) => {
18890
19029
  const fieldProp = deriveShortPropertyName(p.name, group.name);
18891
- return `params += ${ktLiteral(p.name)} to ${prop}.${fieldProp}`;
19030
+ const pair = `${ktLiteral(p.name)} to ${prop}.${fieldProp}`;
19031
+ return receiverMode ? `add(${pair})` : `params += ${pair}`;
18892
19032
  });
18893
19033
  if (entries.length === 1) lines.push(`${indent} is ${sealedName}.${variantName} -> ${entries[0]}`);
18894
19034
  else {
@@ -18986,6 +19126,33 @@ function deduplicateByMount(services, ctx) {
18986
19126
  //#region src/kotlin/tests.ts
18987
19127
  const TEST_PREFIX = "src/test/kotlin/";
18988
19128
  /**
19129
+ * Mirror the ISO-8601 hint promotion the resource/model emitters use so tests
19130
+ * synthesize values whose Kotlin type matches the generated method signature.
19131
+ * Kept in sync with the helpers in `resources.ts` / `models.ts`; if the
19132
+ * detection rule changes, update all three.
19133
+ */
19134
+ const ISO_8601_DESCRIPTION_RE = /\bISO[-_ ]?8601\b/i;
19135
+ function looksLikeIso8601String(description) {
19136
+ if (!description) return false;
19137
+ return ISO_8601_DESCRIPTION_RE.test(description);
19138
+ }
19139
+ function promoteIso8601TypeRef(type, description) {
19140
+ if (!looksLikeIso8601String(description)) return type;
19141
+ const promote = (t) => {
19142
+ if (t.kind === "primitive" && t.type === "string" && !t.format) return {
19143
+ kind: "primitive",
19144
+ type: "string",
19145
+ format: "date-time"
19146
+ };
19147
+ if (t.kind === "nullable") return {
19148
+ kind: "nullable",
19149
+ inner: promote(t.inner)
19150
+ };
19151
+ return t;
19152
+ };
19153
+ return promote(type);
19154
+ }
19155
+ /**
18989
19156
  * Generate one JUnit 5 + WireMock test class per API mount group, plus a
18990
19157
  * cross-cutting model round-trip test.
18991
19158
  *
@@ -19120,10 +19287,11 @@ function buildOperationTest(op, resolved, ctx) {
19120
19287
  }
19121
19288
  for (const qp of sortedQuery) {
19122
19289
  if (!qp.required) break;
19123
- const val = synthValue(qp.type, ctx, imports);
19290
+ const promotedType = promoteIso8601TypeRef(qp.type, qp.description);
19291
+ const val = synthValue(promotedType, ctx, imports);
19124
19292
  if (val === null) return null;
19125
19293
  argParts.push(val);
19126
- const regex = queryValueRegexFor(qp.type);
19294
+ const regex = queryValueRegexFor(promotedType);
19127
19295
  if (regex !== null) requiredQueryAssertions.push({
19128
19296
  name: qp.name,
19129
19297
  valueRegex: regex
@@ -19143,10 +19311,11 @@ function buildOperationTest(op, resolved, ctx) {
19143
19311
  for (const bf of sortedBody) {
19144
19312
  if (sharedQueryBodyParams.has(bf.name)) continue;
19145
19313
  if (!bf.required) break;
19146
- const val = synthValue(bf.type, ctx, imports);
19314
+ const promotedType = promoteIso8601TypeRef(bf.type, bf.description);
19315
+ const val = synthValue(promotedType, ctx, imports);
19147
19316
  if (val === null) return null;
19148
19317
  argParts.push(val);
19149
- if (isScalarBodyField(bf.type)) requiredBodyPaths.push(bf.name);
19318
+ if (isScalarBodyField(promotedType)) requiredBodyPaths.push(bf.name);
19150
19319
  }
19151
19320
  }
19152
19321
  const plan2 = plan;
@@ -19264,7 +19433,7 @@ function buildWrapperTest(op, wrapper, ctx) {
19264
19433
  argParts.push(ktStringLiteral("sample-arg"));
19265
19434
  continue;
19266
19435
  }
19267
- const val = synthValue(rp.field.type, ctx, imports);
19436
+ const val = synthValue(promoteIso8601TypeRef(rp.field.type, rp.field.description), ctx, imports);
19268
19437
  if (val === null) return null;
19269
19438
  argParts.push(val);
19270
19439
  }
@@ -22300,4 +22469,4 @@ const workosEmittersPlugin = {
22300
22469
  //#endregion
22301
22470
  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 };
22302
22471
 
22303
- //# sourceMappingURL=plugin-DOE0FqrZ.mjs.map
22472
+ //# sourceMappingURL=plugin-CeNME04k.mjs.map