@kubb/plugin-oas 4.14.0 → 4.14.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.
@@ -26,6 +26,7 @@ type Context<TOptions, TPluginOptions extends PluginFactoryOptions> = {
26
26
  */
27
27
  plugin: Plugin<TPluginOptions>
28
28
  mode: KubbFile.Mode
29
+ UNSTABLE_NAMING?: true
29
30
  }
30
31
 
31
32
  export class OperationGenerator<
@@ -112,7 +113,6 @@ export class OperationGenerator<
112
113
  } = {},
113
114
  ): OperationSchemas {
114
115
  const operationId = operation.getOperationId({ friendlyCase: true })
115
- const method = operation.method
116
116
  const operationName = transformers.pascalCase(operationId)
117
117
 
118
118
  const resolveKeys = (schema?: SchemaObject) => (schema?.properties ? Object.keys(schema.properties) : undefined)
@@ -127,7 +127,9 @@ export class OperationGenerator<
127
127
  const keys = resolveKeys(schema)
128
128
 
129
129
  return {
130
- name: resolveName(transformers.pascalCase(`${operationId} ${name}`)),
130
+ name: this.context.UNSTABLE_NAMING
131
+ ? resolveName(transformers.pascalCase(`${operationId} status ${name}`))
132
+ : resolveName(transformers.pascalCase(`${operationId} ${name}`)),
131
133
  description: (operation.getResponseByStatusCode(statusCode) as OasTypes.ResponseObject)?.description,
132
134
  schema,
133
135
  operation,
@@ -171,7 +173,9 @@ export class OperationGenerator<
171
173
  : undefined,
172
174
  request: requestSchema
173
175
  ? {
174
- name: resolveName(transformers.pascalCase(`${operationId} ${method === 'get' ? 'queryRequest' : 'mutationRequest'}`)),
176
+ name: this.context.UNSTABLE_NAMING
177
+ ? resolveName(transformers.pascalCase(`${operationId} RequestData`))
178
+ : resolveName(transformers.pascalCase(`${operationId} ${operation.method === 'get' ? 'queryRequest' : 'mutationRequest'}`)),
175
179
  description: (operation.schema.requestBody as OasTypes.RequestBodyObject)?.description,
176
180
  operation,
177
181
  operationName,
@@ -181,7 +185,9 @@ export class OperationGenerator<
181
185
  }
182
186
  : undefined,
183
187
  response: {
184
- name: resolveName(transformers.pascalCase(`${operationId} ${method === 'get' ? 'queryResponse' : 'mutationResponse'}`)),
188
+ name: this.context.UNSTABLE_NAMING
189
+ ? resolveName(transformers.pascalCase(`${operationId} ResponseData`))
190
+ : resolveName(transformers.pascalCase(`${operationId} ${operation.method === 'get' ? 'queryResponse' : 'mutationResponse'}`)),
185
191
  operation,
186
192
  operationName,
187
193
  schema: {
@@ -9,8 +9,7 @@ import type { Fabric } from '@kubb/react-fabric'
9
9
  import pLimit from 'p-limit'
10
10
  import { isDeepEqual, isNumber, uniqueWith } from 'remeda'
11
11
  import type { Generator } from './generators/types.ts'
12
- import type { Schema, SchemaKeywordMapper } from './SchemaMapper.ts'
13
- import { isKeyword, schemaKeywords } from './SchemaMapper.ts'
12
+ import { isKeyword, type Schema, type SchemaKeywordMapper, schemaKeywords } from './SchemaMapper.ts'
14
13
  import type { OperationSchema, Override, Refs } from './types.ts'
15
14
  import { getSchemaFactory } from './utils/getSchemaFactory.ts'
16
15
  import { getSchemas } from './utils/getSchemas.ts'
@@ -2,6 +2,13 @@ import { SchemaGenerator } from './SchemaGenerator.ts'
2
2
  import type { Schema, SchemaKeywordMapper, SchemaMapper, SchemaTree } from './SchemaMapper.ts'
3
3
  import { schemaKeywords } from './SchemaMapper.ts'
4
4
 
5
+ /**
6
+ * Helper type to create a SchemaTree with a specific current schema type
7
+ */
8
+ type SchemaTreeWithKeyword<K extends keyof SchemaKeywordMapper> = Omit<SchemaTree, 'current'> & {
9
+ current: SchemaKeywordMapper[K]
10
+ }
11
+
5
12
  /**
6
13
  * Handler context with parse method available via `this`
7
14
  */
@@ -12,8 +19,13 @@ export type HandlerContext<TOutput, TOptions> = {
12
19
  /**
13
20
  * Handler function type for custom keyword processing
14
21
  * Handlers can access the parse function via `this.parse`
22
+ * The tree.current is typed based on the keyword K
15
23
  */
16
- export type KeywordHandler<TOutput, TOptions> = (this: HandlerContext<TOutput, TOptions>, tree: SchemaTree, options: TOptions) => TOutput | null | undefined
24
+ export type KeywordHandler<TOutput, TOptions, K extends keyof SchemaKeywordMapper = keyof SchemaKeywordMapper> = (
25
+ this: HandlerContext<TOutput, TOptions>,
26
+ tree: SchemaTreeWithKeyword<K>,
27
+ options: TOptions,
28
+ ) => TOutput | null | undefined
17
29
 
18
30
  /**
19
31
  * Configuration for createParser
@@ -51,7 +63,7 @@ export type CreateParserConfig<TOutput, TOptions> = {
51
63
  * - default/describe/optional/nullable: Handle modifiers
52
64
  */
53
65
  handlers: Partial<{
54
- [K in keyof SchemaKeywordMapper]: KeywordHandler<TOutput, TOptions>
66
+ [K in keyof SchemaKeywordMapper]: KeywordHandler<TOutput, TOptions, K>
55
67
  }>
56
68
  }
57
69
 
@@ -65,6 +77,11 @@ export type CreateParserConfig<TOutput, TOptions> = {
65
77
  *
66
78
  * The generated parser is recursive and can handle nested schemas.
67
79
  *
80
+ * **Type Safety**: Each handler receives a `tree` parameter where `tree.current` is automatically
81
+ * typed as the specific schema keyword type (e.g., `SchemaKeywordMapper['ref']` for the `ref` handler).
82
+ * This means you can access `tree.current.args` with full type safety without needing `isKeyword` checks,
83
+ * though such checks can still be used as runtime guards if desired.
84
+ *
68
85
  * @template TOutput - The output type (e.g., string for Zod/Faker, ts.TypeNode for TypeScript)
69
86
  * @template TOptions - The parser options type
70
87
  * @param config - Configuration object containing mapper and handlers
@@ -76,16 +93,23 @@ export type CreateParserConfig<TOutput, TOptions> = {
76
93
  * const parse = createParser({
77
94
  * mapper: zodKeywordMapper,
78
95
  * handlers: {
96
+ * // tree.current is typed as SchemaKeywordMapper['union']
79
97
  * union(tree, options) {
80
- * const items = tree.current.args
98
+ * const items = tree.current.args // args is correctly typed as Schema[]
81
99
  * .map(it => this.parse({ ...tree, current: it }, options))
82
100
  * .filter(Boolean)
83
101
  * return `z.union([${items.join(', ')}])`
84
102
  * },
103
+ * // tree.current is typed as SchemaKeywordMapper['string']
85
104
  * string(tree, options) {
86
105
  * const minSchema = findSchemaKeyword(tree.siblings, 'min')
87
106
  * const maxSchema = findSchemaKeyword(tree.siblings, 'max')
88
107
  * return zodKeywordMapper.string(false, minSchema?.args, maxSchema?.args)
108
+ * },
109
+ * // tree.current is typed as SchemaKeywordMapper['ref']
110
+ * ref(tree, options) {
111
+ * // No need for isKeyword check - tree.current.args is already properly typed
112
+ * return `Ref: ${tree.current.args.name}`
89
113
  * }
90
114
  * }
91
115
  * })
@@ -104,7 +128,9 @@ export function createParser<TOutput, TOptions extends Record<string, any>>(
104
128
  if (handler) {
105
129
  // Create context object with parse method accessible via `this`
106
130
  const context: HandlerContext<TOutput, TOptions> = { parse }
107
- return handler.call(context, tree, options)
131
+ // We need to cast tree here because TypeScript can't statically verify
132
+ // that the handler type matches the current keyword at runtime
133
+ return handler.call(context, tree as any, options)
108
134
  }
109
135
 
110
136
  // Fall back to simple mapper lookup