@kubb/plugin-oas 0.0.0-canary-20240804130641 → 0.0.0-canary-20241104172400
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/README.md +14 -5
- package/dist/OperationGenerator-Bw6sj3Eb.d.cts +560 -0
- package/dist/OperationGenerator-Bw6sj3Eb.d.ts +560 -0
- package/dist/{Schema-D41ItX9C.d.ts → Schema-4FwWfdim.d.cts} +2 -13
- package/dist/{Schema-FGI5VPqc.d.cts → Schema-BlGtSgNB.d.ts} +2 -13
- package/dist/chunk-2TGWPVZN.cjs +92 -0
- package/dist/chunk-2TGWPVZN.cjs.map +1 -0
- package/dist/chunk-A3ROGKLW.cjs +752 -0
- package/dist/chunk-A3ROGKLW.cjs.map +1 -0
- package/dist/chunk-ABOQ73FL.cjs +36 -0
- package/dist/chunk-ABOQ73FL.cjs.map +1 -0
- package/dist/chunk-BG77DP54.js +30 -0
- package/dist/chunk-BG77DP54.js.map +1 -0
- package/dist/chunk-GF26SDHQ.js +28 -0
- package/dist/chunk-GF26SDHQ.js.map +1 -0
- package/dist/chunk-JA75IPYU.js +744 -0
- package/dist/chunk-JA75IPYU.js.map +1 -0
- package/dist/chunk-PADR76WZ.cjs +4 -0
- package/dist/chunk-PADR76WZ.cjs.map +1 -0
- package/dist/chunk-QAFBZLJA.cjs +48 -0
- package/dist/chunk-QAFBZLJA.cjs.map +1 -0
- package/dist/chunk-R47XMJ32.js +3 -0
- package/dist/chunk-R47XMJ32.js.map +1 -0
- package/dist/chunk-TNWNNVQW.js +88 -0
- package/dist/chunk-TNWNNVQW.js.map +1 -0
- package/dist/chunk-XNCEFOE6.js +45 -0
- package/dist/chunk-XNCEFOE6.js.map +1 -0
- package/dist/chunk-ZWHQ54JM.cjs +32 -0
- package/dist/chunk-ZWHQ54JM.cjs.map +1 -0
- package/dist/components.cjs +20 -17
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +12 -9
- package/dist/components.d.ts +12 -9
- package/dist/components.js +3 -17
- package/dist/components.js.map +1 -1
- package/dist/generators.cjs +14 -0
- package/dist/generators.cjs.map +1 -0
- package/dist/generators.d.cts +9 -0
- package/dist/generators.d.ts +9 -0
- package/dist/generators.js +5 -0
- package/dist/generators.js.map +1 -0
- package/dist/hooks.cjs +90 -58
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +37 -10
- package/dist/hooks.d.ts +37 -10
- package/dist/hooks.js +79 -54
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +277 -270
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -78
- package/dist/index.d.ts +6 -78
- package/dist/index.js +235 -255
- package/dist/index.js.map +1 -1
- package/dist/utils.cjs +32 -87
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +8 -43
- package/dist/utils.d.ts +8 -43
- package/dist/utils.js +8 -86
- package/dist/utils.js.map +1 -1
- package/package.json +21 -15
- package/src/OperationGenerator.ts +91 -64
- package/src/SchemaGenerator.ts +102 -22
- package/src/SchemaMapper.ts +24 -5
- package/src/components/Oas.tsx +9 -3
- package/src/components/Operation.tsx +1 -1
- package/src/components/Schema.tsx +2 -99
- package/src/generator.tsx +129 -0
- package/src/generators/index.ts +1 -0
- package/src/generators/jsonGenerator.ts +32 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useOperationManager.ts +56 -30
- package/src/hooks/useSchemaManager.ts +77 -0
- package/src/index.ts +5 -13
- package/src/plugin.ts +73 -69
- package/src/types.ts +29 -27
- package/src/utils/getParams.ts +2 -2
- package/src/utils/getSchemaFactory.ts +1 -1
- package/src/utils/index.ts +2 -2
- package/src/utils/parseFromConfig.ts +7 -7
- package/dist/OperationGenerator-BV6QRtkW.d.cts +0 -60
- package/dist/OperationGenerator-DO5tNGuT.d.ts +0 -60
- package/dist/SchemaMapper-CsBQ6eEx.d.cts +0 -248
- package/dist/SchemaMapper-CsBQ6eEx.d.ts +0 -248
- package/dist/chunk-CCPZYOTX.cjs +0 -695
- package/dist/chunk-CCPZYOTX.cjs.map +0 -1
- package/dist/chunk-CO7DS2KL.cjs +0 -35
- package/dist/chunk-CO7DS2KL.cjs.map +0 -1
- package/dist/chunk-EK7UO5ZW.js +0 -35
- package/dist/chunk-EK7UO5ZW.js.map +0 -1
- package/dist/chunk-KI3GNEKH.js +0 -3265
- package/dist/chunk-KI3GNEKH.js.map +0 -1
- package/dist/chunk-LRJFQCDO.js +0 -101
- package/dist/chunk-LRJFQCDO.js.map +0 -1
- package/dist/chunk-PFN2GSIC.cjs +0 -101
- package/dist/chunk-PFN2GSIC.cjs.map +0 -1
- package/dist/chunk-TL7TBM2U.cjs +0 -3265
- package/dist/chunk-TL7TBM2U.cjs.map +0 -1
- package/dist/chunk-UZLE6NIY.js +0 -695
- package/dist/chunk-UZLE6NIY.js.map +0 -1
- package/dist/types-Db0qp8u-.d.cts +0 -148
- package/dist/types-Db0qp8u-.d.ts +0 -148
- package/src/utils/getGroupedByTagFiles.ts +0 -82
- package/src/utils/refSorter.ts +0 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-oas",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-20241104172400",
|
|
4
4
|
"description": "Generator swagger",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -25,6 +25,11 @@
|
|
|
25
25
|
"require": "./dist/index.cjs",
|
|
26
26
|
"default": "./dist/index.cjs"
|
|
27
27
|
},
|
|
28
|
+
"./generators": {
|
|
29
|
+
"import": "./dist/generators.js",
|
|
30
|
+
"require": "./dist/generators.cjs",
|
|
31
|
+
"default": "./dist/generators.cjs"
|
|
32
|
+
},
|
|
28
33
|
"./utils": {
|
|
29
34
|
"import": "./dist/utils.js",
|
|
30
35
|
"require": "./dist/utils.cjs",
|
|
@@ -56,6 +61,9 @@
|
|
|
56
61
|
],
|
|
57
62
|
"components": [
|
|
58
63
|
"./dist/components.d.ts"
|
|
64
|
+
],
|
|
65
|
+
"generators": [
|
|
66
|
+
"./dist/generators.d.ts"
|
|
59
67
|
]
|
|
60
68
|
}
|
|
61
69
|
},
|
|
@@ -67,27 +75,25 @@
|
|
|
67
75
|
],
|
|
68
76
|
"dependencies": {
|
|
69
77
|
"@stoplight/yaml": "^4.3.0",
|
|
70
|
-
"remeda": "^2.
|
|
71
|
-
"@kubb/core": "0.0.0-canary-
|
|
72
|
-
"@kubb/fs": "0.0.0-canary-
|
|
73
|
-
"@kubb/oas": "0.0.0-canary-
|
|
74
|
-
"@kubb/react": "0.0.0-canary-
|
|
78
|
+
"remeda": "^2.16.0",
|
|
79
|
+
"@kubb/core": "0.0.0-canary-20241104172400",
|
|
80
|
+
"@kubb/fs": "0.0.0-canary-20241104172400",
|
|
81
|
+
"@kubb/oas": "0.0.0-canary-20241104172400",
|
|
82
|
+
"@kubb/react": "0.0.0-canary-20241104172400"
|
|
75
83
|
},
|
|
76
84
|
"devDependencies": {
|
|
77
|
-
"@types/react": "^18.3.
|
|
78
|
-
"@types/react-dom": "^18.3.0",
|
|
85
|
+
"@types/react": "^18.3.12",
|
|
79
86
|
"react": "^18.3.1",
|
|
80
|
-
"tsup": "^8.
|
|
81
|
-
"typescript": "^5.
|
|
82
|
-
"@kubb/config-
|
|
83
|
-
"@kubb/config-
|
|
84
|
-
"@kubb/config-tsup": "0.0.0-canary-20240804130641"
|
|
87
|
+
"tsup": "^8.3.5",
|
|
88
|
+
"typescript": "^5.6.3",
|
|
89
|
+
"@kubb/config-ts": "0.0.0-canary-20241104172400",
|
|
90
|
+
"@kubb/config-tsup": "0.0.0-canary-20241104172400"
|
|
85
91
|
},
|
|
86
92
|
"peerDependencies": {
|
|
87
|
-
"@kubb/react": "0.0.0-canary-
|
|
93
|
+
"@kubb/react": "0.0.0-canary-20241104172400"
|
|
88
94
|
},
|
|
89
95
|
"engines": {
|
|
90
|
-
"node": ">=
|
|
96
|
+
"node": ">=20"
|
|
91
97
|
},
|
|
92
98
|
"publishConfig": {
|
|
93
99
|
"access": "public",
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { type FileMetaBase
|
|
1
|
+
import { BaseGenerator, type FileMetaBase } from '@kubb/core'
|
|
2
2
|
import transformers from '@kubb/core/transformers'
|
|
3
3
|
|
|
4
4
|
import type { PluginFactoryOptions, PluginManager } from '@kubb/core'
|
|
5
5
|
import type * as KubbFile from '@kubb/fs/types'
|
|
6
6
|
|
|
7
7
|
import type { Plugin } from '@kubb/core'
|
|
8
|
-
import type { HttpMethod, Oas, OasTypes, Operation, contentType } from '@kubb/oas'
|
|
8
|
+
import type { HttpMethod, Oas, OasTypes, Operation, SchemaObject, contentType } from '@kubb/oas'
|
|
9
|
+
import type { Generator } from './generator.tsx'
|
|
9
10
|
import type { Exclude, Include, OperationSchemas, OperationsByMethod, Override } from './types.ts'
|
|
10
11
|
|
|
11
|
-
export type GetOperationGeneratorOptions<T extends OperationGenerator<any, any, any>> = T extends OperationGenerator<infer Options, any, any> ? Options : never
|
|
12
|
-
|
|
13
12
|
export type OperationMethodResult<TFileMeta extends FileMetaBase> = Promise<KubbFile.File<TFileMeta> | Array<KubbFile.File<TFileMeta>> | null>
|
|
14
13
|
|
|
15
14
|
type Context<TOptions, TPluginOptions extends PluginFactoryOptions> = {
|
|
@@ -26,11 +25,10 @@ type Context<TOptions, TPluginOptions extends PluginFactoryOptions> = {
|
|
|
26
25
|
mode: KubbFile.Mode
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
export
|
|
30
|
-
TOptions = unknown,
|
|
28
|
+
export class OperationGenerator<
|
|
31
29
|
TPluginOptions extends PluginFactoryOptions = PluginFactoryOptions,
|
|
32
30
|
TFileMeta extends FileMetaBase = FileMetaBase,
|
|
33
|
-
> extends
|
|
31
|
+
> extends BaseGenerator<TPluginOptions['resolvedOptions'], Context<TPluginOptions['resolvedOptions'], TPluginOptions>> {
|
|
34
32
|
#operationsByMethod: OperationsByMethod = {}
|
|
35
33
|
get operationsByMethod(): OperationsByMethod {
|
|
36
34
|
return this.#operationsByMethod
|
|
@@ -40,7 +38,7 @@ export abstract class OperationGenerator<
|
|
|
40
38
|
this.#operationsByMethod = paths
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
#getOptions(operation: Operation, method: HttpMethod): Partial<
|
|
41
|
+
#getOptions(operation: Operation, method: HttpMethod): Partial<TPluginOptions['resolvedOptions']> {
|
|
44
42
|
const { override = [] } = this.context
|
|
45
43
|
|
|
46
44
|
return (
|
|
@@ -65,10 +63,7 @@ export abstract class OperationGenerator<
|
|
|
65
63
|
})?.options || {}
|
|
66
64
|
)
|
|
67
65
|
}
|
|
68
|
-
|
|
69
|
-
*
|
|
70
|
-
* @deprecated
|
|
71
|
-
*/
|
|
66
|
+
|
|
72
67
|
#isExcluded(operation: Operation, method: HttpMethod): boolean {
|
|
73
68
|
const { exclude = [] } = this.context
|
|
74
69
|
let matched = false
|
|
@@ -93,10 +88,7 @@ export abstract class OperationGenerator<
|
|
|
93
88
|
|
|
94
89
|
return matched
|
|
95
90
|
}
|
|
96
|
-
|
|
97
|
-
*
|
|
98
|
-
* @deprecated
|
|
99
|
-
*/
|
|
91
|
+
|
|
100
92
|
#isIncluded(operation: Operation, method: HttpMethod): boolean {
|
|
101
93
|
const { include = [] } = this.context
|
|
102
94
|
let matched = false
|
|
@@ -124,15 +116,16 @@ export abstract class OperationGenerator<
|
|
|
124
116
|
|
|
125
117
|
getSchemas(
|
|
126
118
|
operation: Operation,
|
|
127
|
-
{
|
|
119
|
+
{
|
|
120
|
+
resolveName = (name) => name,
|
|
121
|
+
}: {
|
|
122
|
+
resolveName?: (name: string) => string
|
|
123
|
+
} = {},
|
|
128
124
|
): OperationSchemas {
|
|
129
125
|
const pathParamsSchema = this.context.oas.getParametersSchema(operation, 'path')
|
|
130
126
|
const queryParamsSchema = this.context.oas.getParametersSchema(operation, 'query')
|
|
131
127
|
const headerParamsSchema = this.context.oas.getParametersSchema(operation, 'header')
|
|
132
128
|
const requestSchema = this.context.oas.getRequestSchema(operation)
|
|
133
|
-
const responseStatusCode =
|
|
134
|
-
forStatusCode || (operation.schema.responses && Object.keys(operation.schema.responses).find((key) => key.startsWith('2'))) || 200
|
|
135
|
-
const responseSchema = this.context.oas.getResponseSchema(operation, responseStatusCode)
|
|
136
129
|
const statusCodes = operation.getResponseStatusCodes().map((statusCode) => {
|
|
137
130
|
let name = statusCode
|
|
138
131
|
if (name === 'default') {
|
|
@@ -149,8 +142,15 @@ export abstract class OperationGenerator<
|
|
|
149
142
|
operationName: transformers.pascalCase(`${operation.getOperationId()}`),
|
|
150
143
|
statusCode: name === 'error' ? undefined : Number(statusCode),
|
|
151
144
|
keys: schema?.properties ? Object.keys(schema.properties) : undefined,
|
|
145
|
+
keysToOmit: schema?.properties
|
|
146
|
+
? Object.keys(schema.properties).filter((key) => {
|
|
147
|
+
const item = schema.properties?.[key] as OasTypes.SchemaObject
|
|
148
|
+
return item?.writeOnly
|
|
149
|
+
})
|
|
150
|
+
: undefined,
|
|
152
151
|
}
|
|
153
152
|
})
|
|
153
|
+
const hasResponses = statusCodes.some((item) => item.statusCode?.toString().startsWith('2'))
|
|
154
154
|
|
|
155
155
|
return {
|
|
156
156
|
pathParams: pathParamsSchema
|
|
@@ -199,38 +199,30 @@ export abstract class OperationGenerator<
|
|
|
199
199
|
: undefined,
|
|
200
200
|
response: {
|
|
201
201
|
name: resolveName(transformers.pascalCase(`${operation.getOperationId()} ${operation.method === 'get' ? 'queryResponse' : 'mutationResponse'}`)),
|
|
202
|
-
description: operation.getResponseAsJSONSchema(responseStatusCode)?.at(0)?.description,
|
|
203
202
|
operation,
|
|
204
203
|
operationName: transformers.pascalCase(`${operation.getOperationId()}`),
|
|
205
|
-
schema:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
204
|
+
schema: {
|
|
205
|
+
oneOf: hasResponses
|
|
206
|
+
? statusCodes
|
|
207
|
+
.filter((item) => item.statusCode?.toString().startsWith('2'))
|
|
208
|
+
.map((item) => {
|
|
209
|
+
return {
|
|
210
|
+
...item.schema,
|
|
211
|
+
$ref: resolveName(transformers.pascalCase(`${operation.getOperationId()} ${item.statusCode}`)),
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
: undefined,
|
|
215
|
+
} as SchemaObject,
|
|
214
216
|
},
|
|
217
|
+
responses: statusCodes.filter((item) => item.statusCode?.toString().startsWith('2')),
|
|
215
218
|
errors: statusCodes.filter((item) => item.statusCode?.toString().startsWith('4') || item.statusCode?.toString().startsWith('5')),
|
|
216
219
|
statusCodes,
|
|
217
220
|
}
|
|
218
221
|
}
|
|
219
222
|
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
get: this.get,
|
|
223
|
-
post: this.post,
|
|
224
|
-
patch: this.patch,
|
|
225
|
-
put: this.put,
|
|
226
|
-
delete: this.delete,
|
|
227
|
-
head: undefined,
|
|
228
|
-
options: undefined,
|
|
229
|
-
trace: undefined,
|
|
230
|
-
} as const
|
|
231
|
-
}
|
|
223
|
+
#methods = ['get', 'post', 'patch', 'put', 'delete']
|
|
232
224
|
|
|
233
|
-
async build(): Promise<Array<KubbFile.File<TFileMeta>>> {
|
|
225
|
+
async build(...generators: Array<Generator<TPluginOptions>>): Promise<Array<KubbFile.File<TFileMeta>>> {
|
|
234
226
|
const { oas } = this.context
|
|
235
227
|
|
|
236
228
|
const paths = oas.getPaths()
|
|
@@ -239,7 +231,7 @@ export abstract class OperationGenerator<
|
|
|
239
231
|
|
|
240
232
|
methods.forEach((method) => {
|
|
241
233
|
const operation = oas.operation(path, method)
|
|
242
|
-
if (operation && this.#methods
|
|
234
|
+
if (operation && [this.#methods].some((methods) => method === operation.method)) {
|
|
243
235
|
const isExcluded = this.#isExcluded(operation, method)
|
|
244
236
|
const isIncluded = this.context.include ? this.#isIncluded(operation, method) : true
|
|
245
237
|
|
|
@@ -267,21 +259,43 @@ export abstract class OperationGenerator<
|
|
|
267
259
|
methods.forEach((method) => {
|
|
268
260
|
const { operation } = this.operationsByMethod[path]?.[method]!
|
|
269
261
|
const options = this.#getOptions(operation, method)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
262
|
+
|
|
263
|
+
const methodToCall = this[method as keyof typeof this] as any
|
|
264
|
+
|
|
265
|
+
if (typeof methodToCall === 'function') {
|
|
266
|
+
const promiseMethod = methodToCall?.call(this, operation, {
|
|
267
|
+
...this.options,
|
|
268
|
+
...options,
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
if (promiseMethod) {
|
|
272
|
+
acc.push(promiseMethod)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
274
276
|
const promiseOperation = this.operation.call(this, operation, {
|
|
275
277
|
...this.options,
|
|
276
278
|
...options,
|
|
277
279
|
})
|
|
278
280
|
|
|
279
|
-
if (promiseMethod) {
|
|
280
|
-
acc.push(promiseMethod)
|
|
281
|
-
}
|
|
282
281
|
if (promiseOperation) {
|
|
283
282
|
acc.push(promiseOperation)
|
|
284
283
|
}
|
|
284
|
+
|
|
285
|
+
generators?.forEach((generator) => {
|
|
286
|
+
const promise = generator.operation?.({
|
|
287
|
+
instance: this,
|
|
288
|
+
operation,
|
|
289
|
+
options: {
|
|
290
|
+
...this.options,
|
|
291
|
+
...options,
|
|
292
|
+
},
|
|
293
|
+
} as any) as Promise<Array<KubbFile.File<TFileMeta>>>
|
|
294
|
+
|
|
295
|
+
if (promise) {
|
|
296
|
+
acc.push(promise)
|
|
297
|
+
}
|
|
298
|
+
})
|
|
285
299
|
})
|
|
286
300
|
|
|
287
301
|
return acc
|
|
@@ -291,6 +305,19 @@ export abstract class OperationGenerator<
|
|
|
291
305
|
|
|
292
306
|
promises.push(this.all(operations.flat().filter(Boolean), this.operationsByMethod))
|
|
293
307
|
|
|
308
|
+
generators?.forEach((generator) => {
|
|
309
|
+
const promise = generator.operations?.({
|
|
310
|
+
instance: this,
|
|
311
|
+
operations: operations.flat().filter(Boolean),
|
|
312
|
+
operationsByMethod: this.operationsByMethod,
|
|
313
|
+
options: this.options,
|
|
314
|
+
} as any) as Promise<Array<KubbFile.File<TFileMeta>>>
|
|
315
|
+
|
|
316
|
+
if (promise) {
|
|
317
|
+
promises.push(promise)
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
|
|
294
321
|
const files = await Promise.all(promises)
|
|
295
322
|
|
|
296
323
|
// using .flat because operationGenerator[method] can return a array of files or just one file
|
|
@@ -300,48 +327,48 @@ export abstract class OperationGenerator<
|
|
|
300
327
|
/**
|
|
301
328
|
* Operation
|
|
302
329
|
*/
|
|
303
|
-
async operation(operation: Operation, options:
|
|
304
|
-
return
|
|
330
|
+
async operation(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
331
|
+
return []
|
|
305
332
|
}
|
|
306
333
|
|
|
307
334
|
/**
|
|
308
335
|
* GET
|
|
309
336
|
*/
|
|
310
|
-
async get(operation: Operation, options:
|
|
311
|
-
return
|
|
337
|
+
async get(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
338
|
+
return []
|
|
312
339
|
}
|
|
313
340
|
|
|
314
341
|
/**
|
|
315
342
|
* POST
|
|
316
343
|
*/
|
|
317
|
-
async post(operation: Operation, options:
|
|
318
|
-
return
|
|
344
|
+
async post(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
345
|
+
return []
|
|
319
346
|
}
|
|
320
347
|
/**
|
|
321
348
|
* PATCH
|
|
322
349
|
*/
|
|
323
|
-
async patch(operation: Operation, options:
|
|
324
|
-
return
|
|
350
|
+
async patch(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
351
|
+
return []
|
|
325
352
|
}
|
|
326
353
|
|
|
327
354
|
/**
|
|
328
355
|
* PUT
|
|
329
356
|
*/
|
|
330
|
-
async put(operation: Operation, options:
|
|
331
|
-
return
|
|
357
|
+
async put(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
358
|
+
return []
|
|
332
359
|
}
|
|
333
360
|
|
|
334
361
|
/**
|
|
335
362
|
* DELETE
|
|
336
363
|
*/
|
|
337
|
-
async delete(operation: Operation, options:
|
|
338
|
-
return
|
|
364
|
+
async delete(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
365
|
+
return []
|
|
339
366
|
}
|
|
340
367
|
|
|
341
368
|
/**
|
|
342
369
|
* Combination of GET, POST, PATCH, PUT, DELETE
|
|
343
370
|
*/
|
|
344
371
|
async all(operations: Operation[], paths: OperationsByMethod): OperationMethodResult<TFileMeta> {
|
|
345
|
-
return
|
|
372
|
+
return []
|
|
346
373
|
}
|
|
347
374
|
}
|
package/src/SchemaGenerator.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FileMetaBase
|
|
1
|
+
import { BaseGenerator, type FileMetaBase } from '@kubb/core'
|
|
2
2
|
import transformers, { pascalCase } from '@kubb/core/transformers'
|
|
3
3
|
import { getUniqueName } from '@kubb/core/utils'
|
|
4
4
|
|
|
@@ -13,6 +13,7 @@ import type * as KubbFile from '@kubb/fs/types'
|
|
|
13
13
|
|
|
14
14
|
import type { Oas, OpenAPIV3, SchemaObject, contentType } from '@kubb/oas'
|
|
15
15
|
import type { Schema, SchemaKeywordMapper } from './SchemaMapper.ts'
|
|
16
|
+
import type { Generator } from './generator.tsx'
|
|
16
17
|
import type { OperationSchema, Override, Refs } from './types.ts'
|
|
17
18
|
|
|
18
19
|
export type GetSchemaGeneratorOptions<T extends SchemaGenerator<any, any, any>> = T extends SchemaGenerator<infer Options, any, any> ? Options : never
|
|
@@ -63,11 +64,11 @@ type SchemaProps = {
|
|
|
63
64
|
parentName?: string
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
export
|
|
67
|
+
export class SchemaGenerator<
|
|
67
68
|
TOptions extends SchemaGeneratorOptions = SchemaGeneratorOptions,
|
|
68
69
|
TPluginOptions extends PluginFactoryOptions = PluginFactoryOptions,
|
|
69
70
|
TFileMeta extends FileMetaBase = FileMetaBase,
|
|
70
|
-
> extends
|
|
71
|
+
> extends BaseGenerator<TOptions, Context<TOptions, TPluginOptions>> {
|
|
71
72
|
// Collect the types of all referenced schemas, so we can export them later
|
|
72
73
|
refs: Refs = {}
|
|
73
74
|
|
|
@@ -85,7 +86,7 @@ export abstract class SchemaGenerator<
|
|
|
85
86
|
const defaultSchemas = this.#parseSchemaObject(props)
|
|
86
87
|
const schemas = options.transformers?.schema?.(props, defaultSchemas) || defaultSchemas || []
|
|
87
88
|
|
|
88
|
-
return uniqueWith
|
|
89
|
+
return uniqueWith(schemas, isDeepEqual)
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
deepSearch<T extends keyof SchemaKeywordMapper>(tree: Schema[] | undefined, keyword: T): SchemaKeywordMapper[T][] {
|
|
@@ -301,7 +302,7 @@ export abstract class SchemaGenerator<
|
|
|
301
302
|
|
|
302
303
|
if (additionalProperties) {
|
|
303
304
|
additionalPropertiesSchemas =
|
|
304
|
-
additionalProperties === true
|
|
305
|
+
additionalProperties === true || !Object.keys(additionalProperties).length
|
|
305
306
|
? [{ keyword: this.#getUnknownReturn({ schema, name }) }]
|
|
306
307
|
: this.parse({ schema: additionalProperties as SchemaObject, parentName: name })
|
|
307
308
|
}
|
|
@@ -335,7 +336,7 @@ export abstract class SchemaGenerator<
|
|
|
335
336
|
return [
|
|
336
337
|
{
|
|
337
338
|
keyword: schemaKeywords.ref,
|
|
338
|
-
args: { name: ref.propertyName, path: ref.path },
|
|
339
|
+
args: { name: ref.propertyName, path: ref.path, isImportable: !!this.context.oas.get($ref) },
|
|
339
340
|
},
|
|
340
341
|
]
|
|
341
342
|
}
|
|
@@ -348,7 +349,7 @@ export abstract class SchemaGenerator<
|
|
|
348
349
|
const file = this.context.pluginManager.getFile({
|
|
349
350
|
name: fileName,
|
|
350
351
|
pluginKey: this.context.plugin.key,
|
|
351
|
-
|
|
352
|
+
extname: '.ts',
|
|
352
353
|
})
|
|
353
354
|
|
|
354
355
|
ref = this.refs[$ref] = {
|
|
@@ -360,7 +361,7 @@ export abstract class SchemaGenerator<
|
|
|
360
361
|
return [
|
|
361
362
|
{
|
|
362
363
|
keyword: schemaKeywords.ref,
|
|
363
|
-
args: { name: ref.propertyName, path: ref?.path,
|
|
364
|
+
args: { name: ref.propertyName, path: ref?.path, isImportable: !!this.context.oas.get($ref) },
|
|
364
365
|
},
|
|
365
366
|
]
|
|
366
367
|
}
|
|
@@ -378,12 +379,6 @@ export abstract class SchemaGenerator<
|
|
|
378
379
|
const options = this.#getOptions({ schema: _schema, name })
|
|
379
380
|
const unknownReturn = this.#getUnknownReturn({ schema: _schema, name })
|
|
380
381
|
const { schema, version } = this.#getParsedSchemaObject(_schema)
|
|
381
|
-
const resolvedName = this.context.pluginManager.resolveName({
|
|
382
|
-
name: `${parentName || ''} ${name}`,
|
|
383
|
-
pluginKey: this.context.plugin.key,
|
|
384
|
-
type: 'type',
|
|
385
|
-
})
|
|
386
|
-
|
|
387
382
|
if (!schema) {
|
|
388
383
|
return [{ keyword: unknownReturn }]
|
|
389
384
|
}
|
|
@@ -407,12 +402,16 @@ export abstract class SchemaGenerator<
|
|
|
407
402
|
keyword: schemaKeywords.default,
|
|
408
403
|
args: transformers.stringify(schema.default),
|
|
409
404
|
})
|
|
410
|
-
}
|
|
411
|
-
if (typeof schema.default === 'boolean') {
|
|
405
|
+
} else if (typeof schema.default === 'boolean') {
|
|
412
406
|
baseItems.push({
|
|
413
407
|
keyword: schemaKeywords.default,
|
|
414
408
|
args: schema.default ?? false,
|
|
415
409
|
})
|
|
410
|
+
} else {
|
|
411
|
+
baseItems.push({
|
|
412
|
+
keyword: schemaKeywords.default,
|
|
413
|
+
args: schema.default,
|
|
414
|
+
})
|
|
416
415
|
}
|
|
417
416
|
}
|
|
418
417
|
|
|
@@ -454,10 +453,16 @@ export abstract class SchemaGenerator<
|
|
|
454
453
|
baseItems.push({ keyword: schemaKeywords.readOnly })
|
|
455
454
|
}
|
|
456
455
|
|
|
456
|
+
if (schema.writeOnly) {
|
|
457
|
+
baseItems.push({ keyword: schemaKeywords.writeOnly })
|
|
458
|
+
}
|
|
459
|
+
|
|
457
460
|
if (isReference(schema)) {
|
|
458
461
|
return [
|
|
459
462
|
...this.#getRefAlias(schema),
|
|
460
463
|
nullable && { keyword: schemaKeywords.nullable },
|
|
464
|
+
schema.readOnly && { keyword: schemaKeywords.readOnly },
|
|
465
|
+
schema.writeOnly && { keyword: schemaKeywords.writeOnly },
|
|
461
466
|
{
|
|
462
467
|
keyword: schemaKeywords.schema,
|
|
463
468
|
args: {
|
|
@@ -484,7 +489,20 @@ export abstract class SchemaGenerator<
|
|
|
484
489
|
}),
|
|
485
490
|
}
|
|
486
491
|
if (schemaWithoutOneOf.properties) {
|
|
487
|
-
|
|
492
|
+
const propertySchemas = this.parse({ schema: schemaWithoutOneOf, name, parentName })
|
|
493
|
+
|
|
494
|
+
return [
|
|
495
|
+
{
|
|
496
|
+
...union,
|
|
497
|
+
args: union.args.map((arg) => {
|
|
498
|
+
return {
|
|
499
|
+
keyword: schemaKeywords.and,
|
|
500
|
+
args: [arg, ...propertySchemas],
|
|
501
|
+
}
|
|
502
|
+
}),
|
|
503
|
+
},
|
|
504
|
+
...baseItems,
|
|
505
|
+
]
|
|
488
506
|
}
|
|
489
507
|
|
|
490
508
|
return [union, ...baseItems]
|
|
@@ -619,6 +637,35 @@ export abstract class SchemaGenerator<
|
|
|
619
637
|
]
|
|
620
638
|
}
|
|
621
639
|
|
|
640
|
+
if (schema.type === 'boolean') {
|
|
641
|
+
// we cannot use z.enum when enum type is boolean
|
|
642
|
+
const enumNames = extensionEnums[0]?.find((item) => isKeyword(item, schemaKeywords.enum)) as unknown as SchemaKeywordMapper['enum']
|
|
643
|
+
return [
|
|
644
|
+
{
|
|
645
|
+
keyword: schemaKeywords.enum,
|
|
646
|
+
args: {
|
|
647
|
+
name: enumName,
|
|
648
|
+
typeName,
|
|
649
|
+
asConst: true,
|
|
650
|
+
items: enumNames?.args?.items
|
|
651
|
+
? [...new Set(enumNames.args.items)].map(({ name, value }) => ({
|
|
652
|
+
name,
|
|
653
|
+
value,
|
|
654
|
+
format: 'boolean',
|
|
655
|
+
}))
|
|
656
|
+
: [...new Set(filteredValues)].map((value: string) => {
|
|
657
|
+
return {
|
|
658
|
+
name: value,
|
|
659
|
+
value,
|
|
660
|
+
format: 'boolean',
|
|
661
|
+
}
|
|
662
|
+
}),
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
...baseItems.filter((item) => item.keyword !== schemaKeywords.matches),
|
|
666
|
+
]
|
|
667
|
+
}
|
|
668
|
+
|
|
622
669
|
if (extensionEnums.length > 0 && extensionEnums[0]) {
|
|
623
670
|
return extensionEnums[0]
|
|
624
671
|
}
|
|
@@ -809,6 +856,10 @@ export abstract class SchemaGenerator<
|
|
|
809
856
|
].filter(Boolean)
|
|
810
857
|
}
|
|
811
858
|
|
|
859
|
+
if (!['boolean', 'object', 'number', 'string', 'integer', 'null'].includes(schema.type)) {
|
|
860
|
+
this.context.pluginManager.logger.emit('warning', `Schema type '${schema.type}' is not valid for schema ${parentName}.${name}`)
|
|
861
|
+
}
|
|
862
|
+
|
|
812
863
|
// 'string' | 'number' | 'integer' | 'boolean'
|
|
813
864
|
return [{ keyword: schema.type }, ...baseItems]
|
|
814
865
|
}
|
|
@@ -816,14 +867,20 @@ export abstract class SchemaGenerator<
|
|
|
816
867
|
return [{ keyword: unknownReturn }]
|
|
817
868
|
}
|
|
818
869
|
|
|
819
|
-
async build(): Promise<Array<KubbFile.File<TFileMeta>>> {
|
|
870
|
+
async build(...generators: Array<Generator<TPluginOptions>>): Promise<Array<KubbFile.File<TFileMeta>>> {
|
|
820
871
|
const { oas, contentType, include } = this.context
|
|
821
872
|
|
|
873
|
+
oas.resolveDiscriminators()
|
|
874
|
+
|
|
822
875
|
const schemas = getSchemas({ oas, contentType, includes: include })
|
|
823
876
|
|
|
824
|
-
const promises = Object.entries(schemas).reduce((acc, [name,
|
|
877
|
+
const promises = Object.entries(schemas).reduce((acc, [name, value]) => {
|
|
878
|
+
if (!value) {
|
|
879
|
+
return acc
|
|
880
|
+
}
|
|
881
|
+
|
|
825
882
|
const options = this.#getOptions({ name })
|
|
826
|
-
const promiseOperation = this.schema.call(this, name,
|
|
883
|
+
const promiseOperation = this.schema.call(this, name, value, {
|
|
827
884
|
...this.options,
|
|
828
885
|
...options,
|
|
829
886
|
})
|
|
@@ -832,17 +889,40 @@ export abstract class SchemaGenerator<
|
|
|
832
889
|
acc.push(promiseOperation)
|
|
833
890
|
}
|
|
834
891
|
|
|
892
|
+
generators?.forEach((generator) => {
|
|
893
|
+
const tree = this.parse({ schema: value, name: name })
|
|
894
|
+
|
|
895
|
+
const promise = generator.schema?.({
|
|
896
|
+
instance: this,
|
|
897
|
+
schema: {
|
|
898
|
+
name,
|
|
899
|
+
value,
|
|
900
|
+
tree,
|
|
901
|
+
},
|
|
902
|
+
options: {
|
|
903
|
+
...this.options,
|
|
904
|
+
...options,
|
|
905
|
+
},
|
|
906
|
+
} as any) as Promise<Array<KubbFile.File<TFileMeta>>>
|
|
907
|
+
|
|
908
|
+
if (promise) {
|
|
909
|
+
acc.push(promise)
|
|
910
|
+
}
|
|
911
|
+
})
|
|
912
|
+
|
|
835
913
|
return acc
|
|
836
914
|
}, [] as SchemaMethodResult<TFileMeta>[])
|
|
837
915
|
|
|
838
916
|
const files = await Promise.all(promises)
|
|
839
917
|
|
|
840
|
-
// using .flat because schemaGenerator[method] can return
|
|
918
|
+
// using .flat because schemaGenerator[method] can return an array of files or just one file
|
|
841
919
|
return files.flat().filter(Boolean)
|
|
842
920
|
}
|
|
843
921
|
|
|
844
922
|
/**
|
|
845
923
|
* Schema
|
|
846
924
|
*/
|
|
847
|
-
|
|
925
|
+
async schema(name: string, object: SchemaObject, options: TOptions): SchemaMethodResult<TFileMeta> {
|
|
926
|
+
return []
|
|
927
|
+
}
|
|
848
928
|
}
|
package/src/SchemaMapper.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type SchemaKeywordMapper = {
|
|
|
12
12
|
strict: { keyword: 'strict' }
|
|
13
13
|
url: { keyword: 'url' }
|
|
14
14
|
readOnly: { keyword: 'readOnly' }
|
|
15
|
+
writeOnly: { keyword: 'writeOnly' }
|
|
15
16
|
uuid: { keyword: 'uuid' }
|
|
16
17
|
email: { keyword: 'email' }
|
|
17
18
|
firstName: { keyword: 'firstName' }
|
|
@@ -34,8 +35,8 @@ export type SchemaKeywordMapper = {
|
|
|
34
35
|
asConst: boolean
|
|
35
36
|
items: Array<{
|
|
36
37
|
name: string | number
|
|
37
|
-
format: 'string' | 'number'
|
|
38
|
-
value?: string | number
|
|
38
|
+
format: 'string' | 'number' | 'boolean'
|
|
39
|
+
value?: string | number | boolean
|
|
39
40
|
}>
|
|
40
41
|
}
|
|
41
42
|
}
|
|
@@ -44,14 +45,25 @@ export type SchemaKeywordMapper = {
|
|
|
44
45
|
keyword: 'const'
|
|
45
46
|
args: {
|
|
46
47
|
name: string | number
|
|
47
|
-
format: 'string' | 'number'
|
|
48
|
-
value?: string | number
|
|
48
|
+
format: 'string' | 'number' | 'boolean'
|
|
49
|
+
value?: string | number | boolean
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
union: { keyword: 'union'; args: Schema[] }
|
|
52
53
|
ref: {
|
|
53
54
|
keyword: 'ref'
|
|
54
|
-
args: {
|
|
55
|
+
args: {
|
|
56
|
+
name: string
|
|
57
|
+
/**
|
|
58
|
+
* Full qualified path.
|
|
59
|
+
*/
|
|
60
|
+
path: KubbFile.OptionalPath
|
|
61
|
+
/**
|
|
62
|
+
* When true `File.Import` will be used.
|
|
63
|
+
* When false a reference will be used inside the current file.
|
|
64
|
+
*/
|
|
65
|
+
isImportable: boolean
|
|
66
|
+
}
|
|
55
67
|
}
|
|
56
68
|
matches: { keyword: 'matches'; args?: string }
|
|
57
69
|
boolean: { keyword: 'boolean' }
|
|
@@ -107,6 +119,7 @@ export const schemaKeywords = {
|
|
|
107
119
|
max: 'max',
|
|
108
120
|
optional: 'optional',
|
|
109
121
|
readOnly: 'readOnly',
|
|
122
|
+
writeOnly: 'writeOnly',
|
|
110
123
|
|
|
111
124
|
// custom ones
|
|
112
125
|
object: 'object',
|
|
@@ -140,6 +153,12 @@ export type SchemaKeywordBase<T> = {
|
|
|
140
153
|
|
|
141
154
|
export type Schema = { keyword: string } | SchemaKeywordMapper[keyof SchemaKeywordMapper]
|
|
142
155
|
|
|
156
|
+
export type SchemaTree = {
|
|
157
|
+
parent: Schema | undefined
|
|
158
|
+
current: Schema
|
|
159
|
+
siblings: Schema[]
|
|
160
|
+
}
|
|
161
|
+
|
|
143
162
|
export function isKeyword<T extends Schema, K extends keyof SchemaKeywordMapper>(meta: T, keyword: K): meta is Extract<T, SchemaKeywordMapper[K]> {
|
|
144
163
|
return meta.keyword === keyword
|
|
145
164
|
}
|