@kubb/plugin-fetch 5.0.0-beta.98 → 5.0.0-beta.99

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.cjs CHANGED
@@ -228,33 +228,17 @@ function buildJSDoc(comments, options = {}) {
228
228
  }
229
229
  //#endregion
230
230
  //#region ../../internals/utils/src/url.ts
231
- function transformParam(raw, casing) {
232
- const param = isValidVarName(raw) ? raw : camelCase(raw);
233
- return casing === "camelcase" ? camelCase(param) : param;
234
- }
235
- function toParamsObject(path, { replacer, casing } = {}) {
236
- const params = {};
237
- for (const match of path.matchAll(/\{([^}]+)\}/g)) {
238
- const param = transformParam(match[1], casing);
239
- const key = replacer ? replacer(param) : param;
240
- params[key] = key;
241
- }
242
- return Object.keys(params).length > 0 ? params : null;
231
+ /**
232
+ * Keeps the OpenAPI parameter name as-is when it is already a valid JS identifier, and
233
+ * camelCases it only enough to become one otherwise (for example a hyphenated path segment).
234
+ */
235
+ function transformParam(raw) {
236
+ return isValidVarName(raw) ? raw : camelCase(raw);
243
237
  }
244
238
  /**
245
- * Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
239
+ * Helpers for OpenAPI/Swagger paths.
246
240
  */
247
241
  var Url = class Url {
248
- /**
249
- * Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
250
- *
251
- * @example
252
- * Url.canParse('https://petstore.swagger.io/v2') // true
253
- * Url.canParse('/pet/{petId}') // false
254
- */
255
- static canParse(url, base) {
256
- return URL.canParse(url, base);
257
- }
258
242
  /**
259
243
  * Converts an OpenAPI/Swagger path to Express-style colon syntax.
260
244
  *
@@ -270,15 +254,14 @@ var Url = class Url {
270
254
  * key.
271
255
  *
272
256
  * @example
273
- * Url.toCasedTemplate('/projects/{project_id}', { casing: 'camelcase' }) // '/projects/{projectId}'
257
+ * Url.toSafeTemplate('/user/{monetary-account-id}') // '/user/{monetaryAccountId}'
274
258
  */
275
- static toCasedTemplate(path, { casing } = {}) {
276
- return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name, casing)}}`);
259
+ static toSafeTemplate(path) {
260
+ return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name)}}`);
277
261
  }
278
262
  /**
279
263
  * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
280
- * `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
281
- * and `casing` controls parameter identifier casing.
264
+ * `prefix` is prepended inside the literal, and `replacer` transforms each parameter name.
282
265
  *
283
266
  * @example
284
267
  * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
@@ -286,10 +269,10 @@ var Url = class Url {
286
269
  * @example
287
270
  * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
288
271
  */
289
- static toTemplateString(path, { prefix, replacer, casing } = {}) {
272
+ static toTemplateString(path, { prefix, replacer } = {}) {
290
273
  const result = path.split(/\{([^}]+)\}/).map((part, i) => {
291
274
  if (i % 2 === 0) return part;
292
- const param = transformParam(part, casing);
275
+ const param = transformParam(part);
293
276
  return `\${${replacer ? replacer(param) : param}}`;
294
277
  }).join("");
295
278
  return `\`${prefix ?? ""}${result}\``;
@@ -297,8 +280,8 @@ var Url = class Url {
297
280
  /**
298
281
  * Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
299
282
  * grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
300
- * names are camelCased to match the generated `path` type, and `prefix` is prepended inside the
301
- * literal. Shared by the client and cypress generators that pass a grouped `path` object.
283
+ * names match the generated `path` type, and `prefix` is prepended inside the literal. Shared by
284
+ * the client and cypress generators that pass a grouped `path` object.
302
285
  *
303
286
  * @example
304
287
  * Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
@@ -306,103 +289,28 @@ var Url = class Url {
306
289
  static toGroupedTemplateString(path, { prefix } = {}) {
307
290
  return Url.toTemplateString(path, {
308
291
  prefix,
309
- casing: "camelcase",
310
292
  replacer: (name) => `path.${name}`
311
293
  });
312
294
  }
313
- /**
314
- * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
315
- * expression when `stringify` is set.
316
- *
317
- * @example
318
- * Url.toObject('/pet/{petId}')
319
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
320
- */
321
- static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
322
- const object = {
323
- url: type === "path" ? Url.toPath(path) : Url.toTemplateString(path, {
324
- replacer,
325
- casing
326
- }),
327
- params: toParamsObject(path, {
328
- replacer,
329
- casing
330
- })
331
- };
332
- if (stringify) {
333
- if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
334
- if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
335
- return `{ url: '${object.url}' }`;
336
- }
337
- return object;
338
- }
339
295
  };
340
296
  //#endregion
341
297
  //#region ../../internals/shared/src/params.ts
342
- const caseParamsCache = /* @__PURE__ */ new WeakMap();
343
- /**
344
- * Applies camelCase to parameter names and returns a new array without mutating the input.
345
- *
346
- * Run it before handing parameters to schema builders so output property keys get the right casing
347
- * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
348
- * original array is returned unchanged. Results are cached per input array.
349
- */
350
- function caseParams(params, casing) {
351
- if (!casing) return params;
352
- const cached = caseParamsCache.get(params);
353
- if (cached) return cached;
354
- const result = params.map((param) => ({
355
- ...param,
356
- name: camelCase(param.name)
357
- }));
358
- caseParamsCache.set(params, result);
359
- return result;
360
- }
361
298
  /**
362
- * Drops parameters that collapse to the same property identity once camelCased, keeping the first.
299
+ * Drops parameters that share the same name, keeping the first.
363
300
  *
364
- * Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
365
- * `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
366
- * an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
367
- * identity so the resulting group is collision-free regardless of the names each caller carries.
301
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
302
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
303
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
304
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
368
305
  */
369
- function dedupeByCasedName(params) {
306
+ function dedupeParams(params) {
370
307
  const seen = /* @__PURE__ */ new Set();
371
308
  return params.filter((param) => {
372
- const key = camelCase(param.name);
373
- if (seen.has(key)) return false;
374
- seen.add(key);
309
+ if (seen.has(param.name)) return false;
310
+ seen.add(param.name);
375
311
  return true;
376
312
  });
377
313
  }
378
- function buildParamsMapping(originalParams, mappedParams) {
379
- const mapping = {};
380
- let hasChanged = false;
381
- originalParams.forEach((param, i) => {
382
- const mappedName = mappedParams[i]?.name ?? param.name;
383
- mapping[param.name] = mappedName;
384
- if (param.name !== mappedName) hasChanged = true;
385
- });
386
- return hasChanged ? mapping : null;
387
- }
388
- function toAccess(object, name) {
389
- return isValidVarName(name) ? `${object}.${name}` : `${object}[${JSON.stringify(name)}]`;
390
- }
391
- /**
392
- * Renders the object-literal expression that renames the camelCased keys of a grouped request
393
- * option back to the names the OpenAPI document declares, guarded so an omitted optional group
394
- * stays omitted. Shared by the client and cypress generators, which pass a `buildParamsMapping`
395
- * result and the source expression to read the keys from.
396
- *
397
- * @example
398
- * ```ts
399
- * buildParamsRemapExpression({ source: 'config.query', mapping: { include_deleted: 'includeDeleted' } })
400
- * // 'config.query ? { "include_deleted": config.query.includeDeleted } : config.query'
401
- * ```
402
- */
403
- function buildParamsRemapExpression({ source, mapping }) {
404
- return `${source} ? { ${Object.entries(mapping).map(([originalName, casedName]) => `${JSON.stringify(originalName)}: ${toAccess(source, casedName)}`).join(", ")} } : ${source}`;
405
- }
406
314
  //#endregion
407
315
  //#region ../../internals/shared/src/operation.ts
408
316
  /**
@@ -536,13 +444,15 @@ function buildOperationComments(node, options = {}) {
536
444
  if (!splitLines) return filteredComments;
537
445
  return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
538
446
  }
539
- function getOperationParameters(node, options = {}) {
540
- const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
447
+ function getOperationParameters(node) {
541
448
  return {
542
- path: dedupeByCasedName(params.filter((param) => param.in === "path")),
543
- query: dedupeByCasedName(params.filter((param) => param.in === "query")),
544
- header: dedupeByCasedName(params.filter((param) => param.in === "header")),
545
- cookie: dedupeByCasedName(params.filter((param) => param.in === "cookie"))
449
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path").map((param) => isValidVarName(param.name) ? param : {
450
+ ...param,
451
+ name: camelCase(param.name)
452
+ })),
453
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
454
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
455
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
546
456
  };
547
457
  }
548
458
  function getStatusCodeNumber(statusCode) {
@@ -594,38 +504,6 @@ function createGroupConfig(group) {
594
504
  };
595
505
  }
596
506
  //#endregion
597
- //#region ../../internals/client/src/builders/paramsRemap.ts
598
- /**
599
- * Builds the call-config entries that rename the camelCased `query` and `headers` keys back to the
600
- * names the OpenAPI document declares, so the wire format follows the spec while the generated
601
- * types keep camelCase keys. Returns an empty array when no name changes. Path parameters need no
602
- * remap because the URL template placeholders are renamed in sync with the `path` keys. Emit the
603
- * entries after the `...config` spread so they override the camelCased groups the caller passes in.
604
- *
605
- * @example
606
- * ```ts
607
- * // a query param named include_deleted in the spec
608
- * buildParamsRemap({ node }) // ['query: config.query ? { "include_deleted": config.query.includeDeleted } : config.query']
609
- * ```
610
- */
611
- function buildParamsRemap({ node }) {
612
- if (!kubb_kit.ast.isHttpOperationNode(node)) return [];
613
- const original = getOperationParameters(node, { paramsCasing: "original" });
614
- const cased = getOperationParameters(node);
615
- const queryMapping = buildParamsMapping(original.query, cased.query);
616
- const headerMapping = buildParamsMapping(original.header, cased.header);
617
- const entries = [];
618
- if (queryMapping) entries.push(`query: ${buildParamsRemapExpression({
619
- source: "config.query",
620
- mapping: queryMapping
621
- })}`);
622
- if (headerMapping) entries.push(`headers: ${buildParamsRemapExpression({
623
- source: "config.headers",
624
- mapping: headerMapping
625
- })}`);
626
- return entries;
627
- }
628
- //#endregion
629
507
  //#region ../../internals/client/src/builders/generics.ts
630
508
  /**
631
509
  * Builds the `RequestResult` generic arguments for one operation: the plugin-ts per-status responses
@@ -857,11 +735,10 @@ function buildCallConfig({ node, validator, zodResolver, security }) {
857
735
  const securityLiteral = buildSecurityMetadata({ security });
858
736
  return `{ ${[
859
737
  `method: '${node.method.toUpperCase()}'`,
860
- `url: '${Url.toCasedTemplate(node.path, { casing: "camelcase" })}'`,
738
+ `url: '${Url.toSafeTemplate(node.path)}'`,
861
739
  securityLiteral ? `security: ${securityLiteral}` : null,
862
740
  validatorLiteral,
863
- "...config",
864
- ...buildParamsRemap({ node })
741
+ "...config"
865
742
  ].filter(Boolean).join(", ")} }`;
866
743
  }
867
744
  /**
@@ -987,14 +864,13 @@ function Operation({ name, node, tsResolver, zodResolver, validator, security, i
987
864
  const validatorLiteral = validatorEntries.length ? `validator: { ${validatorEntries.join(", ")} }` : null;
988
865
  const callConfig = `{ ${[
989
866
  `method: '${node.method.toUpperCase()}'`,
990
- `url: '${Url.toCasedTemplate(node.path, { casing: "camelcase" })}'`,
867
+ `url: '${Url.toSafeTemplate(node.path)}'`,
991
868
  securityLiteral ? `security: ${securityLiteral}` : null,
992
869
  stylesLiteral ? `styles: ${stylesLiteral}` : null,
993
870
  validatorLiteral,
994
871
  contentTypeLiteral,
995
872
  responseTypeLiteral,
996
- "...config",
997
- ...buildParamsRemap({ node })
873
+ "...config"
998
874
  ].filter(Boolean).join(", ")} }`;
999
875
  const eventType = `SuccessOf<${tsResolver.response.responses(node)}>`;
1000
876
  const returnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
@@ -1087,7 +963,7 @@ function resolveTypeImportNames(node, tsResolver) {
1087
963
  return [tsResolver.response.options(node), tsResolver.response.responses(node)];
1088
964
  }
1089
965
  function resolveZodImportNames(node, zodResolver, validator) {
1090
- const { query: queryParams } = getOperationParameters(node, { paramsCasing: "original" });
966
+ const { query: queryParams } = getOperationParameters(node);
1091
967
  return [
1092
968
  resolveResponseValidator(validator) === "zod" ? zodResolver.response.response(node) : null,
1093
969
  resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,