@kubb/plugin-swr 5.0.0-beta.77 → 5.0.0-beta.80

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.d.ts CHANGED
@@ -10,7 +10,7 @@ import { Exclude, Group, Include, Output, OutputOptions, Override, PluginFactory
10
10
  * body and query parameters before the call; `response` validates the success response body and,
11
11
  * on the non-throw path, the error body.
12
12
  */
13
- type ParserOptions = false | 'zod' | {
13
+ type ValidatorOptions = false | 'zod' | {
14
14
  request?: 'zod';
15
15
  response?: 'zod';
16
16
  };
@@ -82,7 +82,7 @@ type Options$1 = OutputOptions & {
82
82
  *
83
83
  * @default false
84
84
  */
85
- parser?: ParserOptions;
85
+ validator?: ValidatorOptions;
86
86
  /**
87
87
  * Generates a class-based SDK instead of the standalone functions. Each tag client is an instance
88
88
  * class whose constructor takes a client config and builds its own client, so every environment is
@@ -148,7 +148,7 @@ type ResolvedOptions$1 = {
148
148
  override: Array<Override<ResolvedOptions$1>>;
149
149
  group: Group | null;
150
150
  baseURL: Options$1['baseURL'];
151
- parser: NonNullable<Options$1['parser']>;
151
+ validator: NonNullable<Options$1['validator']>;
152
152
  sdk: {
153
153
  mode: Mode;
154
154
  name: string | undefined;
@@ -321,7 +321,7 @@ type Options = OutputOptions & {
321
321
  /**
322
322
  * Parser to use for validating response data.
323
323
  */
324
- parser?: Options$1['parser'];
324
+ validator?: Options$1['validator'];
325
325
  /**
326
326
  * Override naming conventions for function names and types.
327
327
  */
@@ -349,7 +349,7 @@ type ResolvedOptions = {
349
349
  * The resolved contract client the generators import and call.
350
350
  */
351
351
  client: ResolvedClient;
352
- parser: NonNullable<Options['parser']>;
352
+ validator: NonNullable<Options['validator']>;
353
353
  queryKey: QueryKey | undefined;
354
354
  query: NonNullable<Required<Query>> | false;
355
355
  mutationKey: MutationKey | undefined;
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ function toFilePath(name, caseLast = camelCase) {
62
62
  * JavaScript and Java reserved words.
63
63
  * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
64
64
  */
65
- const reservedWords = new Set([
65
+ const reservedWords = /* @__PURE__ */ new Set([
66
66
  "abstract",
67
67
  "arguments",
68
68
  "boolean",
@@ -291,6 +291,23 @@ function caseParams(params, casing) {
291
291
  caseParamsCache.set(params, result);
292
292
  return result;
293
293
  }
294
+ /**
295
+ * Drops parameters that collapse to the same property identity once camelCased, keeping the first.
296
+ *
297
+ * Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
298
+ * `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
299
+ * an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
300
+ * identity so the resulting group is collision-free regardless of the names each caller carries.
301
+ */
302
+ function dedupeByCasedName(params) {
303
+ const seen = /* @__PURE__ */ new Set();
304
+ return params.filter((param) => {
305
+ const key = camelCase(param.name);
306
+ if (seen.has(key)) return false;
307
+ seen.add(key);
308
+ return true;
309
+ });
310
+ }
294
311
  //#endregion
295
312
  //#region ../../internals/shared/src/operation.ts
296
313
  /**
@@ -329,11 +346,27 @@ function getContentTypeInfo(node) {
329
346
  hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
330
347
  };
331
348
  }
349
+ /**
350
+ * The request-body counterpart for the primary success response: the content types it documents and
351
+ * whether several are present, so the client can let a caller pick which one to accept.
352
+ */
353
+ function getResponseContentTypeInfo(node) {
354
+ const contentTypes = getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? [];
355
+ const isMultipleContentTypes = contentTypes.length > 1;
356
+ return {
357
+ contentTypes,
358
+ isMultipleContentTypes,
359
+ contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
360
+ defaultContentType: contentTypes[0] ?? "application/json",
361
+ hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
362
+ };
363
+ }
332
364
  function buildRequestConfigType(node) {
333
- const { isMultipleContentTypes, contentTypeUnion } = getContentTypeInfo(node);
365
+ const request = getContentTypeInfo(node);
366
+ const response = getResponseContentTypeInfo(node);
334
367
  const configType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
335
- const contentTypeProp = isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : null;
336
- return contentTypeProp ? `${configType} & { ${contentTypeProp} }` : configType;
368
+ const members = [request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null, response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null].filter(Boolean);
369
+ return members.length ? `${configType} & { contentType?: { ${members.join("; ")} } }` : configType;
337
370
  }
338
371
  /**
339
372
  * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,
@@ -395,20 +428,33 @@ function buildOperationComments(node, options = {}) {
395
428
  function getOperationParameters(node, options = {}) {
396
429
  const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
397
430
  return {
398
- path: params.filter((param) => param.in === "path"),
399
- query: params.filter((param) => param.in === "query"),
400
- header: params.filter((param) => param.in === "header"),
401
- cookie: params.filter((param) => param.in === "cookie")
431
+ path: dedupeByCasedName(params.filter((param) => param.in === "path")),
432
+ query: dedupeByCasedName(params.filter((param) => param.in === "query")),
433
+ header: dedupeByCasedName(params.filter((param) => param.in === "header")),
434
+ cookie: dedupeByCasedName(params.filter((param) => param.in === "cookie"))
402
435
  };
403
436
  }
404
437
  function getStatusCodeNumber(statusCode) {
405
438
  const code = Number(statusCode);
406
439
  return Number.isNaN(code) ? null : code;
407
440
  }
441
+ function isSuccessStatusCode(statusCode) {
442
+ const code = getStatusCodeNumber(statusCode);
443
+ return code !== null && code >= 200 && code < 300;
444
+ }
408
445
  function isErrorStatusCode(statusCode) {
409
446
  const code = getStatusCodeNumber(statusCode);
410
447
  return code !== null && code >= 400;
411
448
  }
449
+ function getSuccessResponses(responses) {
450
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
451
+ }
452
+ function getOperationSuccessResponses(node) {
453
+ return getSuccessResponses(node.responses);
454
+ }
455
+ function getPrimarySuccessResponse(node) {
456
+ return getOperationSuccessResponses(node)[0] ?? null;
457
+ }
412
458
  function resolveErrorNames(node, resolver) {
413
459
  return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
414
460
  }
@@ -479,14 +525,14 @@ function createGroupConfig(group) {
479
525
  };
480
526
  }
481
527
  //#endregion
482
- //#region ../../internals/client/src/builders/parser.ts
528
+ //#region ../../internals/client/src/builders/validatorOptions.ts
483
529
  /**
484
- * Returns `true` when any direction of the parser uses zod (used for dependency checks).
530
+ * Returns `true` when any direction of the validator uses zod (used for dependency checks).
485
531
  */
486
- function isParserEnabled(parser) {
487
- if (!parser) return false;
488
- if (parser === "zod") return true;
489
- return Boolean(parser.request || parser.response);
532
+ function isValidatorEnabled(validator) {
533
+ if (!validator) return false;
534
+ if (validator === "zod") return true;
535
+ return Boolean(validator.request || validator.response);
490
536
  }
491
537
  //#endregion
492
538
  //#region ../../internals/client/src/resolveClient.ts
@@ -1016,7 +1062,7 @@ const mutationGenerator = defineGenerator({
1016
1062
  operation(node, ctx) {
1017
1063
  if (!ast.isHttpOperationNode(node)) return null;
1018
1064
  const { config, driver, resolver, root } = ctx;
1019
- const { output, query, mutation, parser, client, group } = ctx.options;
1065
+ const { output, query, mutation, validator, client, group } = ctx.options;
1020
1066
  const pluginTs = driver.getPlugin(pluginTsName);
1021
1067
  if (!pluginTs) return null;
1022
1068
  const tsResolver = driver.getResolver(pluginTsName);
@@ -1062,7 +1108,7 @@ const mutationGenerator = defineGenerator({
1062
1108
  order: "body-response-first",
1063
1109
  includeParams: false
1064
1110
  })].filter((name) => Boolean(name));
1065
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : void 0;
1111
+ const pluginZod = isValidatorEnabled(validator) ? driver.getPlugin(pluginZodName) : void 0;
1066
1112
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : void 0;
1067
1113
  const fileZod = zodResolver ? zodResolver.resolveFile({
1068
1114
  name: node.operationId,
@@ -1074,7 +1120,7 @@ const mutationGenerator = defineGenerator({
1074
1120
  output: pluginZod?.options?.output ?? output,
1075
1121
  group: pluginZod?.options?.group
1076
1122
  }) : void 0;
1077
- const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser);
1123
+ const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, validator);
1078
1124
  const calledClientName = contractOp.name;
1079
1125
  return /* @__PURE__ */ jsxs(File, {
1080
1126
  baseName: meta.file.baseName,
@@ -1157,7 +1203,7 @@ const queryGenerator = defineGenerator({
1157
1203
  operation(node, ctx) {
1158
1204
  if (!ast.isHttpOperationNode(node)) return null;
1159
1205
  const { config, driver, resolver, root } = ctx;
1160
- const { output, query, mutation, parser, client, group } = ctx.options;
1206
+ const { output, query, mutation, validator, client, group } = ctx.options;
1161
1207
  const pluginTs = driver.getPlugin(pluginTsName);
1162
1208
  if (!pluginTs) return null;
1163
1209
  const tsResolver = driver.getResolver(pluginTsName);
@@ -1205,7 +1251,7 @@ const queryGenerator = defineGenerator({
1205
1251
  order: "body-response-first",
1206
1252
  includeParams: false
1207
1253
  })].filter((name) => Boolean(name));
1208
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : void 0;
1254
+ const pluginZod = isValidatorEnabled(validator) ? driver.getPlugin(pluginZodName) : void 0;
1209
1255
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : void 0;
1210
1256
  const fileZod = zodResolver ? zodResolver.resolveFile({
1211
1257
  name: node.operationId,
@@ -1217,7 +1263,7 @@ const queryGenerator = defineGenerator({
1217
1263
  output: pluginZod?.options?.output ?? output,
1218
1264
  group: pluginZod?.options?.group
1219
1265
  }) : void 0;
1220
- const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser);
1266
+ const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, validator);
1221
1267
  const calledClientName = contractOp.name;
1222
1268
  return /* @__PURE__ */ jsxs(File, {
1223
1269
  baseName: meta.file.baseName,
@@ -1358,13 +1404,13 @@ const pluginSwr = definePlugin((options) => {
1358
1404
  const { output = {
1359
1405
  path: "hooks",
1360
1406
  barrel: { type: "named" }
1361
- }, group, exclude = [], include, override = [], parser = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, client, resolver: userResolver, macros: userMacros } = options;
1407
+ }, group, exclude = [], include, override = [], validator = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, client, resolver: userResolver, macros: userMacros } = options;
1362
1408
  const selectedGenerators = [queryGenerator, mutationGenerator].filter((generator) => Boolean(generator));
1363
1409
  const groupConfig = createGroupConfig(group) ?? void 0;
1364
1410
  return {
1365
1411
  name: pluginSwrName,
1366
1412
  options,
1367
- dependencies: [pluginTsName, isParserEnabled(parser) ? pluginZodName : void 0].filter((dependency) => Boolean(dependency)),
1413
+ dependencies: [pluginTsName, isValidatorEnabled(validator) ? pluginZodName : void 0].filter((dependency) => Boolean(dependency)),
1368
1414
  hooks: { "kubb:plugin:setup"(ctx) {
1369
1415
  const resolver = userResolver ? {
1370
1416
  ...resolverSwr,
@@ -1399,7 +1445,7 @@ const pluginSwr = definePlugin((options) => {
1399
1445
  ],
1400
1446
  ...mutation
1401
1447
  },
1402
- parser,
1448
+ validator,
1403
1449
  group: groupConfig,
1404
1450
  exclude,
1405
1451
  include,