@kubb/plugin-fetch 5.0.0-beta.84 → 5.0.0-beta.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -341,6 +341,34 @@ function dedupeByCasedName(params) {
341
341
  return true;
342
342
  });
343
343
  }
344
+ function buildParamsMapping(originalParams, mappedParams) {
345
+ const mapping = {};
346
+ let hasChanged = false;
347
+ originalParams.forEach((param, i) => {
348
+ const mappedName = mappedParams[i]?.name ?? param.name;
349
+ mapping[param.name] = mappedName;
350
+ if (param.name !== mappedName) hasChanged = true;
351
+ });
352
+ return hasChanged ? mapping : null;
353
+ }
354
+ function toAccess(object, name) {
355
+ return isValidVarName(name) ? `${object}.${name}` : `${object}[${JSON.stringify(name)}]`;
356
+ }
357
+ /**
358
+ * Renders the object-literal expression that renames the camelCased keys of a grouped request
359
+ * option back to the names the OpenAPI document declares, guarded so an omitted optional group
360
+ * stays omitted. Shared by the client and cypress generators, which pass a `buildParamsMapping`
361
+ * result and the source expression to read the keys from.
362
+ *
363
+ * @example
364
+ * ```ts
365
+ * buildParamsRemapExpression({ source: 'config.query', mapping: { include_deleted: 'includeDeleted' } })
366
+ * // 'config.query ? { "include_deleted": config.query.includeDeleted } : config.query'
367
+ * ```
368
+ */
369
+ function buildParamsRemapExpression({ source, mapping }) {
370
+ return `${source} ? { ${Object.entries(mapping).map(([originalName, casedName]) => `${JSON.stringify(originalName)}: ${toAccess(source, casedName)}`).join(", ")} } : ${source}`;
371
+ }
344
372
  //#endregion
345
373
  //#region ../../internals/shared/src/operation.ts
346
374
  /**
@@ -662,6 +690,38 @@ function buildSecurityMetadata({ security }) {
662
690
  return `[${security.map(serializeAuth).join(", ")}]`;
663
691
  }
664
692
  //#endregion
693
+ //#region ../../internals/client/src/builders/paramsRemap.ts
694
+ /**
695
+ * Builds the call-config entries that rename the camelCased `query` and `headers` keys back to the
696
+ * names the OpenAPI document declares, so the wire format follows the spec while the generated
697
+ * types keep camelCase keys. Returns an empty array when no name changes. Path parameters need no
698
+ * remap because the URL template placeholders are renamed in sync with the `path` keys. Emit the
699
+ * entries after the `...config` spread so they override the camelCased groups the caller passes in.
700
+ *
701
+ * @example
702
+ * ```ts
703
+ * // a query param named include_deleted in the spec
704
+ * buildParamsRemap({ node }) // ['query: config.query ? { "include_deleted": config.query.includeDeleted } : config.query']
705
+ * ```
706
+ */
707
+ function buildParamsRemap({ node }) {
708
+ if (!ast.isHttpOperationNode(node)) return [];
709
+ const original = getOperationParameters(node, { paramsCasing: "original" });
710
+ const cased = getOperationParameters(node);
711
+ const queryMapping = buildParamsMapping(original.query, cased.query);
712
+ const headerMapping = buildParamsMapping(original.header, cased.header);
713
+ const entries = [];
714
+ if (queryMapping) entries.push(`query: ${buildParamsRemapExpression({
715
+ source: "config.query",
716
+ mapping: queryMapping
717
+ })}`);
718
+ if (headerMapping) entries.push(`headers: ${buildParamsRemapExpression({
719
+ source: "config.headers",
720
+ mapping: headerMapping
721
+ })}`);
722
+ return entries;
723
+ }
724
+ //#endregion
665
725
  //#region ../../internals/client/src/builders/generics.ts
666
726
  /**
667
727
  * Builds the `RequestResult` generic arguments for one operation: the plugin-ts per-status responses
@@ -724,12 +784,13 @@ function buildGroupedOptionsSignature({ node, tsResolver }) {
724
784
  //#endregion
725
785
  //#region ../../internals/client/src/builders/styles.ts
726
786
  /**
727
- * Renders a parameter name as an object-literal key, quoting it when the camelCased name is not a
728
- * bare identifier (for example a name that starts with a digit) so the emitted literal stays valid.
787
+ * Renders a parameter name as an object-literal key, quoted when it is not a bare identifier.
788
+ * Path keys are camelCased to match the URL template placeholders. Query, header, and cookie keys
789
+ * keep the spec name, matching the remapped keys the runtime serializes.
729
790
  */
730
- function toKey(name) {
731
- const cased = camelCase(name);
732
- return isValidVarName(cased) ? cased : JSON.stringify(cased);
791
+ function toKey(name, location) {
792
+ const key = location === "path" ? camelCase(name) : name;
793
+ return isValidVarName(key) ? key : JSON.stringify(key);
733
794
  }
734
795
  /**
735
796
  * Serializes one parameter's metadata into a `{ style, explode }` literal, or `null` when the
@@ -743,8 +804,9 @@ function serializeParameter(parameter) {
743
804
  return parts.length > 0 ? `{ ${parts.join(", ")} }` : null;
744
805
  }
745
806
  /**
746
- * Builds the per-operation `styles` literal from the operation's parameters, grouped by location and
747
- * keyed by the camelCased parameter name to match the generated `path` / `query` / `headers` keys.
807
+ * Builds the per-operation `styles` literal from the operation's parameters, grouped by location.
808
+ * Path entries are keyed by the camelCased name to match the URL template placeholders; query,
809
+ * header, and cookie entries keep the spec name to match the keys the runtime serializes.
748
810
  * Only parameters whose source defines `style` or `explode` are emitted, so calls without
749
811
  * serialization metadata keep the runtime defaults and existing output is unchanged. Returns `null`
750
812
  * when no parameter carries metadata.
@@ -766,7 +828,7 @@ function buildStyles({ node }) {
766
828
  for (const parameter of node.parameters) {
767
829
  const literal = serializeParameter(parameter);
768
830
  if (!literal) continue;
769
- groups[parameter.in].push(`${toKey(parameter.name)}: ${literal}`);
831
+ groups[parameter.in].push(`${toKey(parameter.name, parameter.in)}: ${literal}`);
770
832
  }
771
833
  const locations = Object.keys(groups).filter((location) => groups[location].length > 0);
772
834
  if (locations.length === 0) return null;
@@ -839,7 +901,8 @@ function Operation({ name, node, tsResolver, zodResolver, validator, security, i
839
901
  validatorLiteral,
840
902
  contentTypeLiteral,
841
903
  responseTypeLiteral,
842
- "...config"
904
+ "...config",
905
+ ...buildParamsRemap({ node })
843
906
  ].filter(Boolean).join(", ")} }`;
844
907
  const eventType = `SuccessOf<${tsResolver.resolveResponsesName(node)}>`;
845
908
  const returnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
@@ -892,7 +955,8 @@ function buildCallConfig({ node, validator, zodResolver, security }) {
892
955
  `url: '${Url.toCasedTemplate(node.path, { casing: "camelcase" })}'`,
893
956
  securityLiteral ? `security: ${securityLiteral}` : null,
894
957
  validatorLiteral,
895
- "...config"
958
+ "...config",
959
+ ...buildParamsRemap({ node })
896
960
  ].filter(Boolean).join(", ")} }`;
897
961
  }
898
962
  /**