fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: update custom local client

Lubos ​ a9bffe77 b43e9173

+576 -35
+40
packages/openapi-ts-tests/test/custom/client/core/auth.ts
··· 1 + export type AuthToken = string | undefined; 2 + 3 + export interface Auth { 4 + /** 5 + * Which part of the request do we use to send the auth? 6 + * 7 + * @default 'header' 8 + */ 9 + in?: 'header' | 'query' | 'cookie'; 10 + /** 11 + * Header or query parameter name. 12 + * 13 + * @default 'Authorization' 14 + */ 15 + name?: string; 16 + scheme?: 'basic' | 'bearer'; 17 + type: 'apiKey' | 'http'; 18 + } 19 + 20 + export const getAuthToken = async ( 21 + auth: Auth, 22 + callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken, 23 + ): Promise<string | undefined> => { 24 + const token = 25 + typeof callback === 'function' ? await callback(auth) : callback; 26 + 27 + if (!token) { 28 + return; 29 + } 30 + 31 + if (auth.scheme === 'bearer') { 32 + return `Bearer ${token}`; 33 + } 34 + 35 + if (auth.scheme === 'basic') { 36 + return `Basic ${btoa(token)}`; 37 + } 38 + 39 + return token; 40 + };
+84
packages/openapi-ts-tests/test/custom/client/core/bodySerializer.ts
··· 1 + import type { 2 + ArrayStyle, 3 + ObjectStyle, 4 + SerializerOptions, 5 + } from './pathSerializer'; 6 + 7 + export type QuerySerializer = (query: Record<string, unknown>) => string; 8 + 9 + export type BodySerializer = (body: any) => any; 10 + 11 + export interface QuerySerializerOptions { 12 + allowReserved?: boolean; 13 + array?: SerializerOptions<ArrayStyle>; 14 + object?: SerializerOptions<ObjectStyle>; 15 + } 16 + 17 + const serializeFormDataPair = (data: FormData, key: string, value: unknown) => { 18 + if (typeof value === 'string' || value instanceof Blob) { 19 + data.append(key, value); 20 + } else { 21 + data.append(key, JSON.stringify(value)); 22 + } 23 + }; 24 + 25 + const serializeUrlSearchParamsPair = ( 26 + data: URLSearchParams, 27 + key: string, 28 + value: unknown, 29 + ) => { 30 + if (typeof value === 'string') { 31 + data.append(key, value); 32 + } else { 33 + data.append(key, JSON.stringify(value)); 34 + } 35 + }; 36 + 37 + export const formDataBodySerializer = { 38 + bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>( 39 + body: T, 40 + ) => { 41 + const data = new FormData(); 42 + 43 + Object.entries(body).forEach(([key, value]) => { 44 + if (value === undefined || value === null) { 45 + return; 46 + } 47 + if (Array.isArray(value)) { 48 + value.forEach((v) => serializeFormDataPair(data, key, v)); 49 + } else { 50 + serializeFormDataPair(data, key, value); 51 + } 52 + }); 53 + 54 + return data; 55 + }, 56 + }; 57 + 58 + export const jsonBodySerializer = { 59 + bodySerializer: <T>(body: T) => 60 + JSON.stringify(body, (key, value) => 61 + typeof value === 'bigint' ? value.toString() : value, 62 + ), 63 + }; 64 + 65 + export const urlSearchParamsBodySerializer = { 66 + bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>( 67 + body: T, 68 + ) => { 69 + const data = new URLSearchParams(); 70 + 71 + Object.entries(body).forEach(([key, value]) => { 72 + if (value === undefined || value === null) { 73 + return; 74 + } 75 + if (Array.isArray(value)) { 76 + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); 77 + } else { 78 + serializeUrlSearchParamsPair(data, key, value); 79 + } 80 + }); 81 + 82 + return data.toString(); 83 + }, 84 + };
+141
packages/openapi-ts-tests/test/custom/client/core/params.ts
··· 1 + type Slot = 'body' | 'headers' | 'path' | 'query'; 2 + 3 + export 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 + 15 + export interface Fields { 16 + allowExtra?: Partial<Record<Slot, boolean>>; 17 + args?: ReadonlyArray<Field>; 18 + } 19 + 20 + export type FieldsConfig = ReadonlyArray<Field | Fields>; 21 + 22 + const extraPrefixesMap: Record<string, Slot> = { 23 + $body_: 'body', 24 + $headers_: 'headers', 25 + $path_: 'path', 26 + $query_: 'query', 27 + }; 28 + const extraPrefixes = Object.entries(extraPrefixesMap); 29 + 30 + type KeyMap = Map< 31 + string, 32 + { 33 + in: Slot; 34 + map?: string; 35 + } 36 + >; 37 + 38 + const 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 + 59 + interface Params { 60 + body: unknown; 61 + headers: Record<string, unknown>; 62 + path: Record<string, unknown>; 63 + query: Record<string, unknown>; 64 + } 65 + 66 + const 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 + 74 + export 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 + };
+179
packages/openapi-ts-tests/test/custom/client/core/pathSerializer.ts
··· 1 + interface SerializeOptions<T> 2 + extends SerializePrimitiveOptions, 3 + SerializerOptions<T> {} 4 + 5 + interface SerializePrimitiveOptions { 6 + allowReserved?: boolean; 7 + name: string; 8 + } 9 + 10 + export interface SerializerOptions<T> { 11 + /** 12 + * @default true 13 + */ 14 + explode: boolean; 15 + style: T; 16 + } 17 + 18 + export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; 19 + export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; 20 + type MatrixStyle = 'label' | 'matrix' | 'simple'; 21 + export type ObjectStyle = 'form' | 'deepObject'; 22 + type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; 23 + 24 + interface SerializePrimitiveParam extends SerializePrimitiveOptions { 25 + value: string; 26 + } 27 + 28 + export const separatorArrayExplode = (style: ArraySeparatorStyle) => { 29 + switch (style) { 30 + case 'label': 31 + return '.'; 32 + case 'matrix': 33 + return ';'; 34 + case 'simple': 35 + return ','; 36 + default: 37 + return '&'; 38 + } 39 + }; 40 + 41 + export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { 42 + switch (style) { 43 + case 'form': 44 + return ','; 45 + case 'pipeDelimited': 46 + return '|'; 47 + case 'spaceDelimited': 48 + return '%20'; 49 + default: 50 + return ','; 51 + } 52 + }; 53 + 54 + export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { 55 + switch (style) { 56 + case 'label': 57 + return '.'; 58 + case 'matrix': 59 + return ';'; 60 + case 'simple': 61 + return ','; 62 + default: 63 + return '&'; 64 + } 65 + }; 66 + 67 + export const serializeArrayParam = ({ 68 + allowReserved, 69 + explode, 70 + name, 71 + style, 72 + value, 73 + }: SerializeOptions<ArraySeparatorStyle> & { 74 + value: unknown[]; 75 + }) => { 76 + if (!explode) { 77 + const joinedValues = ( 78 + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) 79 + ).join(separatorArrayNoExplode(style)); 80 + switch (style) { 81 + case 'label': 82 + return `.${joinedValues}`; 83 + case 'matrix': 84 + return `;${name}=${joinedValues}`; 85 + case 'simple': 86 + return joinedValues; 87 + default: 88 + return `${name}=${joinedValues}`; 89 + } 90 + } 91 + 92 + const separator = separatorArrayExplode(style); 93 + const joinedValues = value 94 + .map((v) => { 95 + if (style === 'label' || style === 'simple') { 96 + return allowReserved ? v : encodeURIComponent(v as string); 97 + } 98 + 99 + return serializePrimitiveParam({ 100 + allowReserved, 101 + name, 102 + value: v as string, 103 + }); 104 + }) 105 + .join(separator); 106 + return style === 'label' || style === 'matrix' 107 + ? separator + joinedValues 108 + : joinedValues; 109 + }; 110 + 111 + export const serializePrimitiveParam = ({ 112 + allowReserved, 113 + name, 114 + value, 115 + }: SerializePrimitiveParam) => { 116 + if (value === undefined || value === null) { 117 + return ''; 118 + } 119 + 120 + if (typeof value === 'object') { 121 + throw new Error( 122 + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', 123 + ); 124 + } 125 + 126 + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; 127 + }; 128 + 129 + export const serializeObjectParam = ({ 130 + allowReserved, 131 + explode, 132 + name, 133 + style, 134 + value, 135 + valueOnly, 136 + }: SerializeOptions<ObjectSeparatorStyle> & { 137 + value: Record<string, unknown> | Date; 138 + valueOnly?: boolean; 139 + }) => { 140 + if (value instanceof Date) { 141 + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; 142 + } 143 + 144 + if (style !== 'deepObject' && !explode) { 145 + let values: string[] = []; 146 + Object.entries(value).forEach(([key, v]) => { 147 + values = [ 148 + ...values, 149 + key, 150 + allowReserved ? (v as string) : encodeURIComponent(v as string), 151 + ]; 152 + }); 153 + const joinedValues = values.join(','); 154 + switch (style) { 155 + case 'form': 156 + return `${name}=${joinedValues}`; 157 + case 'label': 158 + return `.${joinedValues}`; 159 + case 'matrix': 160 + return `;${name}=${joinedValues}`; 161 + default: 162 + return joinedValues; 163 + } 164 + } 165 + 166 + const separator = separatorObjectExplode(style); 167 + const joinedValues = Object.entries(value) 168 + .map(([key, v]) => 169 + serializePrimitiveParam({ 170 + allowReserved, 171 + name: style === 'deepObject' ? `${name}[${key}]` : key, 172 + value: v as string, 173 + }), 174 + ) 175 + .join(separator); 176 + return style === 'label' || style === 'matrix' 177 + ? separator + joinedValues 178 + : joinedValues; 179 + };
+98
packages/openapi-ts-tests/test/custom/client/core/types.ts
··· 1 + import type { Auth, AuthToken } from './auth'; 2 + import type { 3 + BodySerializer, 4 + QuerySerializer, 5 + QuerySerializerOptions, 6 + } from './bodySerializer'; 7 + 8 + export interface Client< 9 + RequestFn = never, 10 + Config = unknown, 11 + MethodFn = never, 12 + BuildUrlFn = never, 13 + > { 14 + /** 15 + * Returns the final request URL. 16 + */ 17 + buildUrl: BuildUrlFn; 18 + connect: MethodFn; 19 + delete: MethodFn; 20 + get: MethodFn; 21 + getConfig: () => Config; 22 + head: MethodFn; 23 + options: MethodFn; 24 + patch: MethodFn; 25 + post: MethodFn; 26 + put: MethodFn; 27 + request: RequestFn; 28 + setConfig: (config: Config) => Config; 29 + trace: MethodFn; 30 + } 31 + 32 + export interface Config { 33 + /** 34 + * Auth token or a function returning auth token. The resolved value will be 35 + * added to the request payload as defined by its `security` array. 36 + */ 37 + auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken; 38 + /** 39 + * A function for serializing request body parameter. By default, 40 + * {@link JSON.stringify()} will be used. 41 + */ 42 + bodySerializer?: BodySerializer | null; 43 + /** 44 + * An object containing any HTTP headers that you want to pre-populate your 45 + * `Headers` object with. 46 + * 47 + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} 48 + */ 49 + headers?: 50 + | RequestInit['headers'] 51 + | Record< 52 + string, 53 + | string 54 + | number 55 + | boolean 56 + | (string | number | boolean)[] 57 + | null 58 + | undefined 59 + | unknown 60 + >; 61 + /** 62 + * The request method. 63 + * 64 + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} 65 + */ 66 + method?: 67 + | 'CONNECT' 68 + | 'DELETE' 69 + | 'GET' 70 + | 'HEAD' 71 + | 'OPTIONS' 72 + | 'PATCH' 73 + | 'POST' 74 + | 'PUT' 75 + | 'TRACE'; 76 + /** 77 + * A function for serializing request query parameters. By default, arrays 78 + * will be exploded in form style, objects will be exploded in deepObject 79 + * style, and reserved characters are percent-encoded. 80 + * 81 + * This method will have no effect if the native `paramsSerializer()` Axios 82 + * API function is used. 83 + * 84 + * {@link https://swagger.io/docs/specification/serialization/#query View examples} 85 + */ 86 + querySerializer?: QuerySerializer | QuerySerializerOptions; 87 + /** 88 + * A function transforming response data before it's returned. This is useful 89 + * for post-processing data, e.g. converting ISO strings into Date objects. 90 + */ 91 + responseTransformer?: (data: unknown) => Promise<unknown>; 92 + /** 93 + * A function validating response data. This is useful if you want to ensure 94 + * the response conforms to the desired shape, so it can be safely passed to 95 + * the transformers and returned to the user. 96 + */ 97 + responseValidator?: (data: unknown) => Promise<unknown>; 98 + }
+7 -6
packages/openapi-ts-tests/test/custom/client/index.ts
··· 1 1 export { createClient } from './client'; 2 + export type { Auth } from './core/auth'; 3 + export type { QuerySerializerOptions } from './core/bodySerializer'; 4 + export { 5 + formDataBodySerializer, 6 + jsonBodySerializer, 7 + urlSearchParamsBodySerializer, 8 + } from './core/bodySerializer'; 2 9 export type { 3 10 Client, 4 11 ClientOptions, ··· 11 18 TDataShape, 12 19 } from './types'; 13 20 export { createConfig } from './utils'; 14 - export type { Auth, QuerySerializerOptions } from '@hey-api/client-core'; 15 - export { 16 - formDataBodySerializer, 17 - jsonBodySerializer, 18 - urlSearchParamsBodySerializer, 19 - } from '@hey-api/client-core';
+2 -6
packages/openapi-ts-tests/test/custom/client/types.ts
··· 1 - import type { 2 - Auth, 3 - Client as CoreClient, 4 - Config as CoreConfig, 5 - } from '@hey-api/client-core'; 6 - 1 + import type { Auth } from './core/auth'; 2 + import type { Client as CoreClient, Config as CoreConfig } from './core/types'; 7 3 import type { Middleware } from './utils'; 8 4 9 5 export interface Config<T extends ClientOptions = ClientOptions>
+4 -5
packages/openapi-ts-tests/test/custom/client/utils.ts
··· 1 + import { getAuthToken } from './core/auth'; 1 2 import type { 2 3 QuerySerializer, 3 4 QuerySerializerOptions, 4 - } from '@hey-api/client-core'; 5 + } from './core/bodySerializer'; 6 + import { jsonBodySerializer } from './core/bodySerializer'; 5 7 import { 6 - getAuthToken, 7 - jsonBodySerializer, 8 8 serializeArrayParam, 9 9 serializeObjectParam, 10 10 serializePrimitiveParam, 11 - } from '@hey-api/client-core'; 12 - 11 + } from './core/pathSerializer'; 13 12 import type { Client, ClientOptions, Config, RequestOptions } from './types'; 14 13 15 14 interface PathSerializer {
+3 -5
packages/openapi-ts-tests/test/openapi-ts.config.ts
··· 91 91 // customClientPlugin({ 92 92 // bundle: true, 93 93 // }), 94 - // myClientPlugin({ 95 - // bundle: true, 96 - // }), 94 + myClientPlugin(), 97 95 { 98 96 // baseUrl: false, 99 97 // exportFromIndex: true, 100 - name: '@hey-api/client-nuxt', 98 + // name: '@hey-api/client-nuxt', 101 99 // name: 'legacy/fetch', 102 100 // strictBaseUrl: true, 103 101 }, ··· 147 145 infiniteQueryKeyNameBuilder: '{{name}}IQK', 148 146 infiniteQueryOptionsNameBuilder: '{{name}}InfiniteQuery', 149 147 mutationOptionsNameBuilder: '{{name}}MutationOptions', 150 - name: '@tanstack/react-query', 148 + // name: '@tanstack/react-query', 151 149 queryKeyNameBuilder: '{{name}}QK', 152 150 queryOptionsNameBuilder: '{{name}}Query', 153 151 },
+7 -6
packages/openapi-ts/src/generate/client.ts
··· 72 72 plugin: Plugin.Config<Client.Config & { name: any }>; 73 73 }): void => { 74 74 // copy Hey API clients to output 75 - const isPluginHeyApiClient = plugin.name.startsWith('@hey-api/client-'); 76 - if (isPluginHeyApiClient) { 75 + const isHeyApiClientPlugin = plugin.name.startsWith('@hey-api/client-'); 76 + if (isHeyApiClientPlugin) { 77 77 // copy client core 78 78 const coreOutputPath = path.resolve(outputPath, 'core'); 79 79 ensureDirSync(coreOutputPath); ··· 92 92 return; 93 93 } 94 94 95 - // create folder for client modules 96 - const dirPath = path.resolve(outputPath, 'client'); 97 - ensureDirSync(dirPath); 98 - 99 95 let clientSrcPath = ''; 100 96 if (path.isAbsolute(plugin.name)) { 101 97 clientSrcPath = getClientSrcPath(plugin.name); 102 98 } 103 99 100 + // copy custom local client to output 104 101 if (clientSrcPath) { 102 + const dirPath = path.resolve(outputPath, 'client'); 103 + ensureDirSync(dirPath); 105 104 fs.cpSync(clientSrcPath, dirPath, { 106 105 recursive: true, 107 106 }); ··· 117 116 const indexJsFile = 118 117 clientModulePathComponents[clientModulePathComponents.length - 1]; 119 118 const distFiles = [indexJsFile!, 'index.d.ts', 'index.d.cts']; 119 + const dirPath = path.resolve(outputPath, 'client'); 120 + ensureDirSync(dirPath); 120 121 for (const file of distFiles) { 121 122 fs.copyFileSync( 122 123 path.resolve(clientDistPath, file),
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-axios/__tests__/client.test.ts
··· 1 1 import { describe, expect, it } from 'vitest'; 2 2 3 - import { createClient } from '../client'; 3 + import { createClient } from '../bundle/client'; 4 4 5 5 describe('buildUrl', () => { 6 6 const client = createClient();
+6 -2
packages/openapi-ts/src/plugins/@hey-api/client-axios/__tests__/utils.test.ts
··· 1 - import type { Auth } from '@hey-api/client-core'; 2 1 import { describe, expect, it, vi } from 'vitest'; 3 2 4 - import { axiosHeadersKeywords, mergeHeaders, setAuthParams } from '../utils'; 3 + import type { Auth } from '../../client-core/bundle/auth'; 4 + import { 5 + axiosHeadersKeywords, 6 + mergeHeaders, 7 + setAuthParams, 8 + } from '../bundle/utils'; 5 9 6 10 describe('mergeHeaders', () => { 7 11 it.each(axiosHeadersKeywords)(
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-fetch/__tests__/utils.test.ts
··· 1 - import type { Auth } from '@hey-api/client-core'; 2 1 import { describe, expect, it, vi } from 'vitest'; 3 2 3 + import type { Auth } from '../../client-core/bundle/auth'; 4 4 import type { Client } from '../bundle/types'; 5 5 import { buildUrl, getParseAs, setAuthParams } from '../bundle/utils'; 6 6
+1 -1
packages/openapi-ts/src/plugins/@hey-api/client-next/__tests__/client.test.ts
··· 1 1 import { describe, expect, it } from 'vitest'; 2 2 3 - import { createClient } from '../client'; 3 + import { createClient } from '../bundle/client'; 4 4 5 5 describe('buildUrl', () => { 6 6 const client = createClient();
+2 -2
packages/openapi-ts/src/plugins/@hey-api/client-next/__tests__/utils.test.ts
··· 1 - import type { Auth } from '@hey-api/client-core'; 2 1 import { describe, expect, it, vi } from 'vitest'; 3 2 4 - import { getParseAs, setAuthParams } from '../utils'; 3 + import type { Auth } from '../../client-core/bundle/auth'; 4 + import { getParseAs, setAuthParams } from '../bundle/utils'; 5 5 6 6 describe('getParseAs', () => { 7 7 const scenarios: Array<{