@kubb/oas 4.27.2 → 4.27.4
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 +12 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -6
- package/dist/index.d.ts +4 -6
- package/dist/index.js +14 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +0 -1
- package/src/utils.spec.ts +69 -0
- package/src/utils.ts +16 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/oas",
|
|
3
|
-
"version": "4.27.
|
|
3
|
+
"version": "4.27.4",
|
|
4
4
|
"description": "OpenAPI Specification (OAS) utilities and helpers for Kubb, providing parsing, normalization, and manipulation of OpenAPI/Swagger schemas.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openapi",
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
}
|
|
54
54
|
],
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@
|
|
56
|
+
"@readme/openapi-parser": "^5.5.0",
|
|
57
57
|
"jsonpointer": "^5.0.1",
|
|
58
58
|
"oas": "^28.9.0",
|
|
59
59
|
"oas-normalize": "^15.7.1",
|
|
60
60
|
"openapi-types": "^12.1.3",
|
|
61
61
|
"remeda": "^2.33.6",
|
|
62
62
|
"swagger2openapi": "^7.0.8",
|
|
63
|
-
"@kubb/core": "4.27.
|
|
63
|
+
"@kubb/core": "4.27.4"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@stoplight/yaml": "^4.3.0",
|
package/src/index.ts
CHANGED
package/src/utils.spec.ts
CHANGED
|
@@ -82,6 +82,75 @@ components:
|
|
|
82
82
|
)
|
|
83
83
|
expect(oas.api?.info.title).toBe('Swagger PetStore')
|
|
84
84
|
})
|
|
85
|
+
|
|
86
|
+
test('parse a spec with an external file $ref resolves the ref via bundling', async () => {
|
|
87
|
+
const specPath = path.resolve(__dirname, '../mocks/petStoreExternalFileRef.yaml')
|
|
88
|
+
const oas = await parse(specPath)
|
|
89
|
+
|
|
90
|
+
expect(oas).toBeDefined()
|
|
91
|
+
expect(oas.api?.info.title).toBe('PetStore with external file ref')
|
|
92
|
+
// After bundling, the external file $ref is resolved: the Category schema is inlined
|
|
93
|
+
const petSchema = (oas.api as any).components?.schemas?.Pet
|
|
94
|
+
expect(petSchema).toBeDefined()
|
|
95
|
+
expect(petSchema.type).toBe('object')
|
|
96
|
+
expect(petSchema.required).toEqual(['id', 'name'])
|
|
97
|
+
expect(petSchema.properties.id).toEqual({ type: 'integer', format: 'int64' })
|
|
98
|
+
expect(petSchema.properties.name).toEqual({ type: 'string' })
|
|
99
|
+
// The external file $ref is resolved: category has the inline schema from category.yaml
|
|
100
|
+
const category = petSchema.properties.category
|
|
101
|
+
expect(category).toBeDefined()
|
|
102
|
+
expect(category.$ref).toBeUndefined()
|
|
103
|
+
expect(category.type).toBe('object')
|
|
104
|
+
expect(category.properties.id).toEqual({ type: 'integer', format: 'int64' })
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('parse a spec with an external URL $ref resolves the ref via bundling', async () => {
|
|
108
|
+
const specPath = path.resolve(__dirname, '../../plugin-ts/mocks/petStore.yaml')
|
|
109
|
+
const oas = await parse(specPath)
|
|
110
|
+
|
|
111
|
+
expect(oas).toBeDefined()
|
|
112
|
+
expect(oas.api?.info.title).toBe('Swagger PetStore')
|
|
113
|
+
const petSchema = (oas.api as any).components?.schemas?.Pet
|
|
114
|
+
expect(petSchema).toBeDefined()
|
|
115
|
+
expect(petSchema.type).toBe('object')
|
|
116
|
+
expect(petSchema.required).toEqual(['id', 'name'])
|
|
117
|
+
expect(petSchema.properties.id).toEqual({ type: 'integer', format: 'int64' })
|
|
118
|
+
expect(petSchema.properties.name).toEqual({ type: 'string' })
|
|
119
|
+
expect(petSchema.properties.tag).toEqual({ type: 'string' })
|
|
120
|
+
// After bundling the URL $ref is resolved (inlined) when network is available;
|
|
121
|
+
// on network failure the fallback plain load preserves the original $ref pointer.
|
|
122
|
+
expect(petSchema.properties.category).toBeDefined()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('dereference a spec with external $refs works after bundling', async () => {
|
|
126
|
+
const specPath = path.resolve(__dirname, '../mocks/petStoreExternalFileRef.yaml')
|
|
127
|
+
const oas = await parse(specPath)
|
|
128
|
+
|
|
129
|
+
// After bundling, external file refs are resolved inline so dereference() should work without issues
|
|
130
|
+
await expect(oas.dereference()).resolves.not.toThrow()
|
|
131
|
+
|
|
132
|
+
const petSchema = (oas.api as any).components?.schemas?.Pet
|
|
133
|
+
expect(petSchema).toBeDefined()
|
|
134
|
+
expect(petSchema.type).toBe('object')
|
|
135
|
+
// After bundling, the category is inlined (no external $ref remaining)
|
|
136
|
+
const category = petSchema.properties.category
|
|
137
|
+
expect(category).toBeDefined()
|
|
138
|
+
expect(category.$ref).toBeUndefined()
|
|
139
|
+
expect(category.type).toBe('object')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test('dereference a spec with an external URL $ref works after bundling', async () => {
|
|
143
|
+
const specPath = path.resolve(__dirname, '../../plugin-ts/mocks/petStore.yaml')
|
|
144
|
+
const oas = await parse(specPath)
|
|
145
|
+
|
|
146
|
+
// After bundling (or fallback to load on network error), dereference() should work
|
|
147
|
+
await expect(oas.dereference()).resolves.not.toThrow()
|
|
148
|
+
|
|
149
|
+
const petSchema = (oas.api as any).components?.schemas?.Pet
|
|
150
|
+
expect(petSchema).toBeDefined()
|
|
151
|
+
expect(petSchema.type).toBe('object')
|
|
152
|
+
expect(petSchema.properties.category).toBeDefined()
|
|
153
|
+
})
|
|
85
154
|
})
|
|
86
155
|
|
|
87
156
|
describe('parseFromConfig', () => {
|
package/src/utils.ts
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'node:path'
|
|
|
2
2
|
import type { Config } from '@kubb/core'
|
|
3
3
|
import { pascalCase } from '@kubb/core/transformers'
|
|
4
4
|
import { URLPath } from '@kubb/core/utils'
|
|
5
|
-
import { bundle
|
|
5
|
+
import { bundle } from '@readme/openapi-parser'
|
|
6
6
|
import yaml from '@stoplight/yaml'
|
|
7
7
|
import type { ParameterObject, SchemaObject } from 'oas/types'
|
|
8
8
|
import { isRef, isSchema } from 'oas/types'
|
|
@@ -163,14 +163,21 @@ export function getDefaultValue(schema?: SchemaObject): string | undefined {
|
|
|
163
163
|
|
|
164
164
|
export async function parse(
|
|
165
165
|
pathOrApi: string | Document,
|
|
166
|
-
{ oasClass = Oas,
|
|
166
|
+
{ oasClass = Oas, enablePaths = true }: { oasClass?: typeof Oas; canBundle?: boolean; enablePaths?: boolean } = {},
|
|
167
167
|
): Promise<Oas> {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
168
|
+
// Only attempt to bundle when pathOrApi is a file path or URL (not inline YAML/JSON strings).
|
|
169
|
+
// Inline content (YAML strings with newlines, JSON strings starting with '{') is handled by plain load.
|
|
170
|
+
const isPathOrUrl = typeof pathOrApi === 'string' && !pathOrApi.match(/\n/) && !pathOrApi.match(/^\s*\{/)
|
|
171
|
+
if (isPathOrUrl && enablePaths) {
|
|
172
|
+
// Bundle the spec using the string path directly so that relative external $refs
|
|
173
|
+
// (file and URL) are resolved with the correct base path context.
|
|
174
|
+
try {
|
|
175
|
+
const bundled = await bundle(pathOrApi)
|
|
176
|
+
return parse(bundled as Document, { oasClass, enablePaths })
|
|
177
|
+
} catch (e) {
|
|
178
|
+
// If bundling fails (e.g. unresolvable refs or network error), fall through to plain load
|
|
179
|
+
console.warn(`[kubb] Failed to bundle external $refs in "${pathOrApi}": ${(e as Error).message}. Falling back to plain load.`)
|
|
180
|
+
}
|
|
174
181
|
}
|
|
175
182
|
|
|
176
183
|
const oasNormalize = new OASNormalize(pathOrApi, {
|
|
@@ -191,7 +198,7 @@ export async function parse(
|
|
|
191
198
|
}
|
|
192
199
|
|
|
193
200
|
export async function merge(pathOrApi: Array<string | Document>, { oasClass = Oas }: { oasClass?: typeof Oas } = {}): Promise<Oas> {
|
|
194
|
-
const instances = await Promise.all(pathOrApi.map((p) => parse(p, { oasClass, enablePaths: false
|
|
201
|
+
const instances = await Promise.all(pathOrApi.map((p) => parse(p, { oasClass, enablePaths: false })))
|
|
195
202
|
|
|
196
203
|
if (instances.length === 0) {
|
|
197
204
|
throw new Error('No OAS instances provided for merging.')
|