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 = (
106 args: ReadonlyArray<unknown>,
107 fields: FieldsConfig,
108) => {
109 const params: Params = {
110 body: {},
111 headers: {},
112 path: {},
113 query: {},
114 };
115
116 const map = buildKeyMap(fields);
117
118 let config: FieldsConfig[number] | undefined;
119
120 for (const [index, arg] of args.entries()) {
121 if (fields[index]) {
122 config = fields[index];
123 }
124
125 if (!config) {
126 continue;
127 }
128
129 if ('in' in config) {
130 if (config.key) {
131 const field = map.get(config.key)!;
132 const name = field.map || config.key;
133 if (field.in) {
134 (params[field.in] as Record<string, unknown>)[name] = arg;
135 }
136 } else {
137 params.body = arg;
138 }
139 } else {
140 for (const [key, value] of Object.entries(arg ?? {})) {
141 const field = map.get(key);
142
143 if (field) {
144 if (field.in) {
145 const name = field.map || key;
146 (params[field.in] as Record<string, unknown>)[name] = value;
147 } else {
148 params[field.map] = value;
149 }
150 } else {
151 const extra = extraPrefixes.find(([prefix]) =>
152 key.startsWith(prefix),
153 );
154
155 if (extra) {
156 const [prefix, slot] = extra;
157 (params[slot] as Record<string, unknown>)[
158 key.slice(prefix.length)
159 ] = value;
160 } else if ('allowExtra' in config && config.allowExtra) {
161 for (const [slot, allowed] of Object.entries(config.allowExtra)) {
162 if (allowed) {
163 (params[slot as Slot] as Record<string, unknown>)[key] = value;
164 break;
165 }
166 }
167 }
168 }
169 }
170 }
171 }
172
173 stripEmptySlots(params);
174
175 return params;
176};