Skip to content

Commit d7888ac

Browse files
committed
refactor(generator): create openapi-utils.ts with shared utilities
1 parent 33e3066 commit d7888ac

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import type { OpenAPIV3_1 } from 'openapi-types'
2+
3+
// =============================================================================
4+
// Constants
5+
// =============================================================================
6+
7+
/**
8+
* Content type priority order: json > urlencoded > multipart.
9+
* Used to determine which content type to use when multiple are available.
10+
*/
11+
export const CONTENT_TYPE_PRIORITY = [
12+
'application/json',
13+
'application/x-www-form-urlencoded',
14+
'multipart/form-data',
15+
] as const
16+
17+
// =============================================================================
18+
// $ref Resolution
19+
// =============================================================================
20+
21+
/**
22+
* Resolve a JSON `$ref` reference within an OpenAPI document.
23+
* Handles `#/` prefix stripping, path segment traversal, and null safety.
24+
*/
25+
export function resolveRef<T>(
26+
ref: string,
27+
document: OpenAPIV3_1.Document,
28+
): T | null {
29+
if (!ref.startsWith('#/')) {
30+
return null
31+
}
32+
33+
const parts = ref.slice(2).split('/')
34+
let current: unknown = document
35+
36+
for (const part of parts) {
37+
if (current && typeof current === 'object' && part in current) {
38+
current = (current as Record<string, unknown>)[part]
39+
} else {
40+
return null
41+
}
42+
}
43+
44+
if (current && typeof current === 'object' && !('$ref' in current)) {
45+
return current as T
46+
}
47+
48+
return null
49+
}
50+
51+
// =============================================================================
52+
// Content Type Helpers
53+
// =============================================================================
54+
55+
/**
56+
* Get the first matching request body content from priority-ordered content types.
57+
* Returns the `MediaTypeObject` for the highest-priority content type found.
58+
*/
59+
export function getRequestBodyContent(
60+
content: OpenAPIV3_1.RequestBodyObject['content'] | undefined,
61+
): OpenAPIV3_1.MediaTypeObject | undefined {
62+
if (!content) return undefined
63+
for (const ct of CONTENT_TYPE_PRIORITY) {
64+
if (content[ct]) return content[ct]
65+
}
66+
return undefined
67+
}
68+
69+
// =============================================================================
70+
// Schema Name Extraction
71+
// =============================================================================
72+
73+
/**
74+
* Extract the schema name from a `$ref` string pointing to `#/components/schemas/`.
75+
*/
76+
export function extractSchemaNameFromRef(ref: string): string | null {
77+
if (ref.startsWith('#/components/schemas/')) {
78+
return ref.replace('#/components/schemas/', '')
79+
}
80+
return null
81+
}
82+
83+
// =============================================================================
84+
// Server Name Normalization
85+
// =============================================================================
86+
87+
/**
88+
* Normalize server name by removing the `./` prefix.
89+
*/
90+
export function normalizeServerName(serverName: string): string {
91+
return serverName.replace(/^\.\//, '')
92+
}
93+
94+
// =============================================================================
95+
// Status Code Classification
96+
// =============================================================================
97+
98+
/**
99+
* Check if an HTTP status code represents an error response (4xx, 5xx, or 'default').
100+
*/
101+
export function isErrorStatusCode(statusCode: string): boolean {
102+
if (statusCode === 'default') return true
103+
const code = parseInt(statusCode, 10)
104+
return code >= 400 && code < 600
105+
}

0 commit comments

Comments
 (0)