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