@kubb/adapter-oas 5.0.0-beta.62 → 5.0.0-beta.63

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/adapter-oas",
3
- "version": "5.0.0-beta.62",
3
+ "version": "5.0.0-beta.63",
4
4
  "description": "OpenAPI and Swagger adapter for Kubb. Parses and validates OAS 2.0/3.x specifications into a @kubb/ast RootNode for downstream code generation plugins.",
5
5
  "keywords": [
6
6
  "adapter",
@@ -46,8 +46,8 @@
46
46
  "api-ref-bundler": "0.5.1",
47
47
  "swagger2openapi": "^7.0.8",
48
48
  "yaml": "^2.9.0",
49
- "@kubb/ast": "5.0.0-beta.62",
50
- "@kubb/core": "5.0.0-beta.62"
49
+ "@kubb/ast": "5.0.0-beta.63",
50
+ "@kubb/core": "5.0.0-beta.63"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/json-schema": "^7.0.15",
package/src/adapter.ts CHANGED
@@ -10,7 +10,7 @@ import { collect, narrowSchema } from '@kubb/ast'
10
10
  import { extractRefName } from '@kubb/ast/utils'
11
11
 
12
12
  /**
13
- * Canonical adapter name for `@kubb/adapter-oas`. Used for driver lookups.
13
+ * The `name` of `@kubb/adapter-oas`, used to identify this adapter in a Kubb config.
14
14
  */
15
15
  export const adapterOasName = 'oas' satisfies AdapterOas['name']
16
16
 
package/src/constants.ts CHANGED
@@ -5,10 +5,9 @@ import { ast } from '@kubb/core'
5
5
  *
6
6
  * @example
7
7
  * ```ts
8
- * import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
8
+ * import { DEFAULT_PARSER_OPTIONS, parseOas } from '@kubb/adapter-oas'
9
9
  *
10
- * const parser = createOasParser(oas)
11
- * const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
10
+ * const { root } = parseOas(document, { ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
12
11
  * ```
13
12
  */
14
13
  export const DEFAULT_PARSER_OPTIONS = {
@@ -68,13 +67,21 @@ export const MERGE_DEFAULT_VERSION = '1.0.0' as const
68
67
  */
69
68
  export const structuralKeys = new Set(['properties', 'items', 'additionalProperties', 'oneOf', 'anyOf', 'allOf', 'not'] as const)
70
69
 
70
+ /**
71
+ * Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
72
+ * `int64` and the date/time family. Keep this in sync with the `convertFormat`
73
+ * special-cases in `parser.ts`. `isHandledFormat` reads it so the
74
+ * `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
75
+ */
76
+ export const specialCasedFormats: ReadonlySet<string> = new Set(['int64', 'date-time', 'date', 'time'])
77
+
71
78
  /**
72
79
  * Static map from OAS `format` strings to Kubb `SchemaType` values.
73
80
  *
74
81
  * Only formats whose AST type differs from the OAS `type` field appear here.
75
- * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
76
- * in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname` and
77
- * `idn-hostname` map to `'url'` as the closest generic string-format type.
82
+ * Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
83
+ * separately in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname`
84
+ * and `idn-hostname` map to `'url'` as the closest generic string-format type.
78
85
  *
79
86
  * @example
80
87
  * ```ts
@@ -85,14 +92,6 @@ export const structuralKeys = new Set(['properties', 'items', 'additionalPropert
85
92
  * formatMap['float'] // 'number'
86
93
  * ```
87
94
  */
88
- /**
89
- * Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
90
- * `int64` and the date/time family. Keep this in sync with the `convertFormat`
91
- * special-cases in `parser.ts`. `isHandledFormat` reads it so the
92
- * `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
93
- */
94
- export const specialCasedFormats: ReadonlySet<string> = new Set(['int64', 'date-time', 'date', 'time'])
95
-
96
95
  export const formatMap = {
97
96
  uuid: 'uuid',
98
97
  email: 'email',
package/src/dedupe.ts ADDED
@@ -0,0 +1,237 @@
1
+ import { containsCircularRef, extractRefName } from '@kubb/ast/utils'
2
+ import { ast } from '@kubb/core'
3
+ import { SCHEMA_REF_PREFIX } from './constants.ts'
4
+
5
+ /**
6
+ * The destination a deduplicated shape points at: the shared schema name and the
7
+ * synthetic `$ref` path stored on the generated `ref` nodes.
8
+ */
9
+ type Target = {
10
+ name: string
11
+ ref: string
12
+ }
13
+
14
+ /**
15
+ * The result of {@link plan}: the shared definitions to prepend to the schema list, plus the
16
+ * rewriting behavior that repoints duplicate shapes at their shared target. The lookup maps stay
17
+ * private to the closure, so callers interact with one object instead of three collections.
18
+ */
19
+ export type Plan = {
20
+ /**
21
+ * New top-level schema definitions created for inline shapes that had no existing named
22
+ * component. Nested duplicates inside each definition are already collapsed.
23
+ */
24
+ extracted: Array<ast.SchemaNode>
25
+ /**
26
+ * Rewrites an operation or nested schema, replacing every duplicate sub-schema with a `ref` to
27
+ * its shared target. Replacing a node prunes its subtree, so nested duplicates inside a replaced
28
+ * shape are not visited again. A `ref` to a duplicate top-level schema is repointed at the first
29
+ * schema with the same content.
30
+ */
31
+ apply<T extends ast.Node>(node: T): T
32
+ /**
33
+ * Rewrites a top-level schema. A schema whose content duplicates a different shared one becomes a
34
+ * `ref` alias to it (keeping its own name and docs). Otherwise its nested duplicates collapse
35
+ * while its own root is preserved.
36
+ */
37
+ applyTopLevel(node: ast.SchemaNode): ast.SchemaNode
38
+ /**
39
+ * Whether a top-level name duplicates an earlier schema with the same content. Such a schema is
40
+ * never emitted, since every reference to it is repointed at the first schema with that content.
41
+ */
42
+ isAlias(name: string): boolean
43
+ }
44
+
45
+ /**
46
+ * Runtime state {@link plan} reads from the current parse: schema names that take part in a
47
+ * circular chain (never extracted, extracting them would break the cycle) and the names already in
48
+ * use (so extracted definitions resolve collisions).
49
+ */
50
+ export type DedupeContext = {
51
+ circularSchemas: ReadonlySet<string>
52
+ usedNames: Set<string>
53
+ }
54
+
55
+ /**
56
+ * Minimum occurrences before a shape is deduplicated.
57
+ */
58
+ const MIN_OCCURRENCES = 2
59
+
60
+ /**
61
+ * Builds the shared `ref` replacement for a duplicate occurrence, carrying the
62
+ * usage-slot and documentation fields that are not part of the shared type.
63
+ */
64
+ function createRefNode(node: ast.SchemaNode, target: Target): ast.SchemaNode {
65
+ return ast.factory.createSchema({
66
+ type: 'ref',
67
+ name: target.name,
68
+ ref: target.ref,
69
+ optional: node.optional,
70
+ nullish: node.nullish,
71
+ readOnly: node.readOnly,
72
+ writeOnly: node.writeOnly,
73
+ deprecated: node.deprecated,
74
+ description: node.description,
75
+ default: node.default,
76
+ example: node.example,
77
+ })
78
+ }
79
+
80
+ /**
81
+ * Strips usage-slot flags from an extracted definition and applies its name.
82
+ * A standalone definition is never optional, so `optional`/`nullish` are cleared.
83
+ */
84
+ function cleanDefinition(node: ast.SchemaNode, name: string): ast.SchemaNode {
85
+ return { ...node, name, optional: undefined, nullish: undefined }
86
+ }
87
+
88
+ /**
89
+ * Returns `true` when a node is eligible for deduplication. Only enums and objects qualify, and
90
+ * object shapes that take part in a circular chain are rejected so recursive structures are not
91
+ * extracted (which would break the cycle).
92
+ */
93
+ function isCandidate(node: ast.SchemaNode, circularSchemas: ReadonlySet<string>): boolean {
94
+ if (node.type === 'enum') return true
95
+ if (node.type !== 'object') return false
96
+ if (node.name && circularSchemas.has(node.name)) return false
97
+ return !containsCircularRef(node, { circularSchemas })
98
+ }
99
+
100
+ /**
101
+ * Produces the name for an inline shape with no existing named component, resolving
102
+ * collisions against `usedNames`. Returns `null` for an unnamed shape, which stays inline.
103
+ */
104
+ function nameFor(node: ast.SchemaNode, usedNames: Set<string>): string | null {
105
+ const base = node.name
106
+ if (!base) return null
107
+
108
+ let name = base
109
+ let counter = 2
110
+ while (usedNames.has(name)) {
111
+ name = `${base}${counter++}`
112
+ }
113
+ usedNames.add(name)
114
+ return name
115
+ }
116
+
117
+ /**
118
+ * Scans a forest of schema and operation nodes and produces a {@link Plan}.
119
+ *
120
+ * A shape that occurs at least {@link MIN_OCCURRENCES} times is deduplicated: if any occurrence is
121
+ * a named top-level schema, the first one becomes the target (so other top-level duplicates and
122
+ * inline copies turn into references to it). Other top-level names with the same content are
123
+ * recorded as aliases. Otherwise a new definition is extracted using {@link nameFor}. The returned
124
+ * plan rewrites nodes against those decisions.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const dedupePlan = plan([...schemaNodes, ...operationNodes], { circularSchemas, usedNames })
129
+ * ```
130
+ */
131
+ export function plan(roots: ReadonlyArray<ast.Node>, context: DedupeContext): Plan {
132
+ const { circularSchemas, usedNames } = context
133
+
134
+ const topLevelNodes = new Set<ast.SchemaNode>()
135
+
136
+ type Group = {
137
+ count: number
138
+ representative: ast.SchemaNode
139
+ topLevelNames: Array<string>
140
+ }
141
+ const groups = new Map<string, Group>()
142
+
143
+ for (const root of roots) {
144
+ if (root.kind === 'Schema') topLevelNodes.add(root)
145
+ for (const schemaNode of ast.collect<ast.SchemaNode>(root, { schema: (node) => node })) {
146
+ if (!isCandidate(schemaNode, circularSchemas)) continue
147
+
148
+ const signature = ast.signatureOf(schemaNode)
149
+ const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name
150
+ const group = groups.get(signature)
151
+ if (group) {
152
+ group.count++
153
+ if (isTopLevel) group.topLevelNames.push(schemaNode.name!)
154
+ } else {
155
+ groups.set(signature, { count: 1, representative: schemaNode, topLevelNames: isTopLevel ? [schemaNode.name!] : [] })
156
+ }
157
+ }
158
+ }
159
+
160
+ const bySignature = new Map<string, Target>()
161
+ const byName = new Map<string, Target>()
162
+ const pendingExtractions: Array<{ name: string; representative: ast.SchemaNode }> = []
163
+
164
+ for (const [signature, group] of groups) {
165
+ if (group.count < MIN_OCCURRENCES) continue
166
+
167
+ const [firstName, ...duplicateNames] = group.topLevelNames
168
+ if (firstName) {
169
+ const target: Target = { name: firstName, ref: `${SCHEMA_REF_PREFIX}${firstName}` }
170
+ bySignature.set(signature, target)
171
+ for (const duplicate of duplicateNames) {
172
+ byName.set(duplicate, target)
173
+ }
174
+ continue
175
+ }
176
+
177
+ const name = nameFor(group.representative, usedNames)
178
+ if (!name) continue
179
+
180
+ bySignature.set(signature, { name, ref: `${SCHEMA_REF_PREFIX}${name}` })
181
+ pendingExtractions.push({ name, representative: group.representative })
182
+ }
183
+
184
+ // Rewrites a node against the resolved targets. `skipRootMatch` keeps a definition's own root from
185
+ // turning into a reference to itself. Nested duplicates are still collapsed.
186
+ function rewrite<T extends ast.Node>(node: T, skipRootMatch: boolean): T {
187
+ if (bySignature.size === 0 && byName.size === 0) return node
188
+
189
+ const root = node
190
+
191
+ return ast.transform(node, {
192
+ schema(schemaNode) {
193
+ if (schemaNode.type === 'ref') {
194
+ const refName = schemaNode.ref ? extractRefName(schemaNode.ref) : schemaNode.name
195
+ const target = refName ? byName.get(refName) : undefined
196
+
197
+ return target ? { ...schemaNode, name: target.name, ref: target.ref } : undefined
198
+ }
199
+
200
+ if (skipRootMatch && schemaNode === root) return undefined
201
+
202
+ const target = bySignature.get(ast.signatureOf(schemaNode))
203
+ if (!target) return undefined
204
+
205
+ return createRefNode(schemaNode, target)
206
+ },
207
+ }) as T
208
+ }
209
+
210
+ // Build extracted definitions only after every target name is known, so nested
211
+ // duplicates inside a definition also resolve to refs.
212
+ const extracted = pendingExtractions.map(({ name, representative }) => cleanDefinition(rewrite(representative, true), name))
213
+
214
+ return {
215
+ extracted,
216
+ apply<T extends ast.Node>(node: T): T {
217
+ return rewrite(node, false)
218
+ },
219
+ applyTopLevel(node: ast.SchemaNode): ast.SchemaNode {
220
+ const target = bySignature.get(ast.signatureOf(node))
221
+ if (target && target.name !== node.name) {
222
+ return ast.factory.createSchema({
223
+ type: 'ref',
224
+ name: node.name ?? null,
225
+ ref: target.ref,
226
+ description: node.description,
227
+ deprecated: node.deprecated,
228
+ })
229
+ }
230
+
231
+ return rewrite(node, true)
232
+ },
233
+ isAlias(name: string): boolean {
234
+ return byName.has(name)
235
+ },
236
+ }
237
+ }
package/src/dialect.ts CHANGED
@@ -1,27 +1,9 @@
1
1
  import { ast } from '@kubb/core'
2
+ import { plan } from './dedupe.ts'
2
3
  import { isDiscriminator, isNullable, isReference } from './guards.ts'
3
4
  import { resolveRef } from './refs.ts'
4
5
  import type { SchemaObject } from './types.ts'
5
6
 
6
- /**
7
- * Derives a schema's `optional`/`nullish` flags from a parent's `required` value and the
8
- * schema's own `nullable`. How "required" and "nullable" combine is OpenAPI-specific, so it
9
- * lives in the dialect.
10
- *
11
- * - Non-required + non-nullable → `optional: true`.
12
- * - Non-required + nullable → `nullish: true`.
13
- * - Required → both flags cleared.
14
- */
15
- function optionality(schema: ast.SchemaNode, required: boolean): ast.SchemaNode {
16
- const nullable = schema.nullable ?? false
17
-
18
- return {
19
- ...schema,
20
- optional: !required && !nullable ? true : undefined,
21
- nullish: !required && nullable ? true : undefined,
22
- }
23
- }
24
-
25
7
  /**
26
8
  * The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
27
9
  *
@@ -49,8 +31,8 @@ export const oasDialect = ast.defineDialect({
49
31
  isDiscriminator,
50
32
  isBinary: (schema: SchemaObject) => schema.type === 'string' && schema.contentMediaType === 'application/octet-stream',
51
33
  resolveRef,
52
- optionality,
53
34
  },
35
+ dedupe: { plan },
54
36
  })
55
37
 
56
38
  /**
@@ -7,7 +7,7 @@ export type DiscriminatorTarget = {
7
7
  }
8
8
 
9
9
  /**
10
- * Builds a map of child schema names discriminator patch data by scanning the given
10
+ * Maps each child schema name to its discriminator patch data by scanning the given
11
11
  * top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
12
12
  *
13
13
  * The streaming path calls this on a small pre-parsed subset of schemas (only the
package/src/factory.ts CHANGED
@@ -18,7 +18,7 @@ export type ValidateDocumentOptions = {
18
18
  }
19
19
 
20
20
  /**
21
- * Loads and dereferences an OpenAPI document, returning the raw `Document`.
21
+ * Loads and bundles an OpenAPI document, returning the raw `Document`.
22
22
  *
23
23
  * Accepts a file path string or an already-parsed document object. File paths and URLs are
24
24
  * bundled via `api-ref-bundler`, hoisting external file schemas into named `components.schemas`
package/src/mime.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * MIME type fragments that mark a media type as JSON-like.
3
3
  *
4
- * Matches the substrings used by the `oas` library's `matchesMimeType.json`, so a content type is
5
- * JSON when it contains any of these. The `+json` entry catches structured-syntax suffixes such as
6
- * `application/vnd.api+json`.
4
+ * A content type is JSON when it contains any of these substrings. The `+json` entry catches
5
+ * structured-syntax suffixes such as `application/vnd.api+json`.
7
6
  */
8
7
  const jsonMimeFragments = ['application/json', 'application/x-json', 'text/json', 'text/x-json', '+json'] as const
9
8
 
package/src/operation.ts CHANGED
@@ -125,8 +125,8 @@ export function getRequestContent({
125
125
  }
126
126
 
127
127
  /**
128
- * Returns the primary request content type. Prefers a JSON-like media type (the last one wins,
129
- * matching the previous behavior), then the first declared one, defaulting to `'application/json'`.
128
+ * Returns the primary request content type. Prefers a JSON-like media type (the last one wins
129
+ * when several are declared), then the first declared one, defaulting to `'application/json'`.
130
130
  */
131
131
  export function getRequestContentType({ document, operation }: OperationContext): string {
132
132
  const content = getRequestBodyContent({ document, operation })
package/src/parser.ts CHANGED
@@ -606,17 +606,14 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
606
606
  const propNode = parseSchema({ schema: resolvedPropSchema, name: resolvedChildName }, rawOptions)
607
607
  const schemaNode = nameEnums(propNode, { parentName: name, propName, enumSuffix: options.enumSuffix })
608
608
 
609
- return ast.factory.createProperty(
610
- {
611
- name: propName,
612
- schema: {
613
- ...schemaNode,
614
- nullable: schemaNode.type === 'null' ? undefined : propNullable || undefined,
615
- },
616
- required,
609
+ return ast.factory.createProperty({
610
+ name: propName,
611
+ schema: {
612
+ ...schemaNode,
613
+ nullable: schemaNode.type === 'null' ? undefined : propNullable || undefined,
617
614
  },
618
- dialect,
619
- )
615
+ required,
616
+ })
620
617
  })
621
618
  : []
622
619
 
@@ -894,18 +891,15 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
894
891
  ? parseSchema({ schema: param['schema'] as SchemaObject, name: schemaName }, options)
895
892
  : ast.factory.createSchema({ type: typeOptionMap.get(options.unknownType)! })
896
893
 
897
- return ast.factory.createParameter(
898
- {
899
- name: paramName,
900
- in: param['in'] as ast.ParameterLocation,
901
- schema: {
902
- ...schema,
903
- description: (param['description'] as string | undefined) ?? schema.description,
904
- },
905
- required,
894
+ return ast.factory.createParameter({
895
+ name: paramName,
896
+ in: param['in'] as ast.ParameterLocation,
897
+ schema: {
898
+ ...schema,
899
+ description: (param['description'] as string | undefined) ?? schema.description,
906
900
  },
907
- dialect,
908
- )
901
+ required,
902
+ })
909
903
  }
910
904
 
911
905
  /**
@@ -984,7 +978,7 @@ export function createSchemaParser(ctx: OasParserContext, dialect: OasDialect =
984
978
  return [
985
979
  ast.factory.createContent({
986
980
  contentType: ct,
987
- schema: dialect.schema.optionality(parseSchema({ schema, name: requestBodyName }, options), requestBodyMeta.required),
981
+ schema: ast.optionality(parseSchema({ schema, name: requestBodyName }, options), requestBodyMeta.required),
988
982
  keysToOmit: collectPropertyKeysByFlag(schema, 'readOnly'),
989
983
  }),
990
984
  ]
package/src/refs.ts CHANGED
@@ -7,12 +7,13 @@ const _refCache = new WeakMap<Document, Map<string, unknown>>()
7
7
  /**
8
8
  * Resolves a local JSON pointer reference from a document.
9
9
  *
10
- * Accepts `#/...` refs. Returns `null` for empty or non-local refs.
11
- * Throws when the pointer cannot be resolved.
10
+ * Accepts `#/...` refs. Returns `null` for an empty or non-local ref. When the pointer cannot be
11
+ * resolved, reports a `refNotFound` diagnostic into the active build and returns `null`. Outside a
12
+ * build there is no sink to collect it, so it throws instead.
12
13
  *
13
14
  * @example
14
15
  * ```ts
15
- * resolveRef<SchemaObject>(document, '#/components/schemas/Pet') // SchemaObject | null
16
+ * resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
16
17
  * ```
17
18
  */
18
19
  export function resolveRef<T = unknown>(document: Document, $ref: string): T | null {
package/src/resolvers.ts CHANGED
@@ -59,9 +59,9 @@ export function getSchemaType(format: string): ast.SchemaType | null {
59
59
  }
60
60
 
61
61
  /**
62
- * Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry and the
63
- * `specialCasedFormats` `convertFormat` handles directly. False means the format falls back to the
64
- * base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
62
+ * Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry, plus the
63
+ * `specialCasedFormats` that `convertFormat` handles directly. False means the format falls back to
64
+ * the base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
65
65
  * diagnostic in step with the parser as `formatMap` grows.
66
66
  */
67
67
  export function isHandledFormat(format: string): boolean {
@@ -86,7 +86,7 @@ export type OperationsOptions = {
86
86
  /**
87
87
  * Returns all parameters for an operation, merging path-level and operation-level entries.
88
88
  * Operation-level parameters override path-level ones with the same `in:name` key.
89
- * `$ref` parameters resolve via `dereferenceWithRef` for backward compatibility.
89
+ * Each `$ref` parameter is dereferenced via `dereferenceWithRef` before merging.
90
90
  *
91
91
  * @example
92
92
  * ```ts
@@ -220,7 +220,8 @@ export function getRequestSchema(document: Document, operation: Operation, optio
220
220
  type SchemaSourceMode = 'schemas' | 'responses' | 'requestBodies'
221
221
 
222
222
  /**
223
- * A schema annotated with its component section source and original name. Used by `resolveNameCollisions` for cross-source collision resolution.
223
+ * A schema annotated with its component section source and original name. `getSchemas` uses this
224
+ * to resolve name collisions across sources.
224
225
  */
225
226
  type SchemaWithMetadata = {
226
227
  schema: SchemaObject
@@ -234,6 +235,10 @@ export type GetSchemasOptions = {
234
235
 
235
236
  export type GetSchemasResult = {
236
237
  schemas: Record<string, SchemaObject>
238
+ /**
239
+ * Maps each original component pointer (`#/components/<source>/<name>`) to the
240
+ * collision-resolved unique name used as the key in `schemas`.
241
+ */
237
242
  nameMapping: Map<string, string>
238
243
  }
239
244
 
@@ -248,7 +253,10 @@ export type GetSchemasResult = {
248
253
  * ```ts
249
254
  * flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
250
255
  * // { type: 'object', properties: {}, description: 'A pet' }
256
+ * ```
251
257
  *
258
+ * @example
259
+ * ```ts
252
260
  * flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
253
261
  * // returned unchanged, contains a $ref
254
262
  * ```