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 = (args: ReadonlyArray<unknown>, fields: FieldsConfig) => {
75 const params: Params = {
76 body: {},
77 headers: {},
78 path: {},
79 query: {},
80 };
81
82 const map = buildKeyMap(fields);
83
84 let config: FieldsConfig[number] | undefined;
85
86 for (const [index, arg] of args.entries()) {
87 if (fields[index]) {
88 config = fields[index];
89 }
90
91 if (!config) {
92 continue;
93 }
94
95 if ('in' in config) {
96 if (config.key) {
97 const field = map.get(config.key)!;
98 const name = field.map || config.key;
99 (params[field.in] as Record<string, unknown>)[name] = arg;
100 } else {
101 params.body = arg;
102 }
103 } else {
104 for (const [key, value] of Object.entries(arg ?? {})) {
105 const field = map.get(key);
106
107 if (field) {
108 const name = field.map || key;
109 (params[field.in] as Record<string, unknown>)[name] = value;
110 } else {
111 const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix));
112
113 if (extra) {
114 const [prefix, slot] = extra;
115 (params[slot] as Record<string, unknown>)[key.slice(prefix.length)] = value;
116 } else {
117 for (const [slot, allowed] of Object.entries(config.allowExtra ?? {})) {
118 if (allowed) {
119 (params[slot as Slot] as Record<string, unknown>)[key] = value;
120 break;
121 }
122 }
123 }
124 }
125 }
126 }
127 }
128
129 stripEmptySlots(params);
130
131 return params;
132};