fork of hey-api/openapi-ts because I need some additional things
1// This file is auto-generated by @hey-api/openapi-ts
2
3type Slot = 'body' | 'headers' | 'path' | 'query'
4
5export type Field =
6 | {
7 in: Exclude<Slot, 'body'>
8 /**
9 * Field name. This is the name we want the user to see and use.
10 */
11 key: string
12 /**
13 * Field mapped name. This is the name we want to use in the request.
14 * If omitted, we use the same value as `key`.
15 */
16 map?: string
17 }
18 | {
19 in: Extract<Slot, 'body'>
20 /**
21 * Key isn't required for bodies.
22 */
23 key?: string
24 map?: string
25 }
26 | {
27 /**
28 * Field name. This is the name we want the user to see and use.
29 */
30 key: string
31 /**
32 * Field mapped name. This is the name we want to use in the request.
33 * If `in` is omitted, `map` aliases `key` to the transport layer.
34 */
35 map: Slot
36 }
37
38export interface Fields {
39 allowExtra?: Partial<Record<Slot, boolean>>
40 args?: ReadonlyArray<Field>
41}
42
43export type FieldsConfig = ReadonlyArray<Field | Fields>
44
45const extraPrefixesMap: Record<string, Slot> = {
46 $body_: 'body',
47 $headers_: 'headers',
48 $path_: 'path',
49 $query_: 'query'
50}
51const extraPrefixes = Object.entries(extraPrefixesMap)
52
53type KeyMap = Map<
54 string,
55 | {
56 in: Slot
57 map?: string
58 }
59 | {
60 in?: never
61 map: Slot
62 }
63>
64
65const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
66 if (!map) {
67 map = new Map()
68 }
69
70 for (const config of fields) {
71 if ('in' in config) {
72 if (config.key) {
73 map.set(config.key, {
74 in: config.in,
75 map: config.map
76 })
77 }
78 } else if ('key' in config) {
79 map.set(config.key, {
80 map: config.map
81 })
82 } else if (config.args) {
83 buildKeyMap(config.args, map)
84 }
85 }
86
87 return map
88}
89
90interface Params {
91 body: unknown
92 headers: Record<string, unknown>
93 path: Record<string, unknown>
94 query: Record<string, unknown>
95}
96
97const stripEmptySlots = (params: Params) => {
98 for (const [slot, value] of Object.entries(params)) {
99 if (value && typeof value === 'object' && !Object.keys(value).length) {
100 delete params[slot as Slot]
101 }
102 }
103}
104
105export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsConfig) => {
106 const params: Params = {
107 body: {},
108 headers: {},
109 path: {},
110 query: {}
111 }
112
113 const map = buildKeyMap(fields)
114
115 let config: FieldsConfig[number] | undefined
116
117 for (const [index, arg] of args.entries()) {
118 if (fields[index]) {
119 config = fields[index]
120 }
121
122 if (!config) {
123 continue
124 }
125
126 if ('in' in config) {
127 if (config.key) {
128 const field = map.get(config.key)!
129 const name = field.map || config.key
130 if (field.in) {
131 ;(params[field.in] as Record<string, unknown>)[name] = arg
132 }
133 } else {
134 params.body = arg
135 }
136 } else {
137 for (const [key, value] of Object.entries(arg ?? {})) {
138 const field = map.get(key)
139
140 if (field) {
141 if (field.in) {
142 const name = field.map || key
143 ;(params[field.in] as Record<string, unknown>)[name] = value
144 } else {
145 params[field.map] = value
146 }
147 } else {
148 const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix))
149
150 if (extra) {
151 const [prefix, slot] = extra
152 ;(params[slot] as Record<string, unknown>)[key.slice(prefix.length)] = value
153 } else if ('allowExtra' in config && config.allowExtra) {
154 for (const [slot, allowed] of Object.entries(config.allowExtra)) {
155 if (allowed) {
156 ;(params[slot as Slot] as Record<string, unknown>)[key] = value
157 break
158 }
159 }
160 }
161 }
162 }
163 }
164 }
165
166 stripEmptySlots(params)
167
168 return params
169}