···11+---
22+'@hey-api/openapi-ts': patch
33+---
44+55+feat: support multiple configurations
+102-25
docs/openapi-ts/configuration.md
···38383939Alternatively, you can use `openapi-ts.config.js` and configure the export statement depending on your project setup.
40404141-<!--
4242-TODO: uncomment after c12 supports multiple configs
4343-see https://github.com/unjs/c12/issues/92
4444--->
4545-<!-- ### Multiple Clients
4646-4747-If you want to generate multiple clients with a single `openapi-ts` command, you can provide an array of configuration objects.
4848-4949-```js
5050-import { defineConfig } from '@hey-api/openapi-ts';
5151-5252-export default defineConfig([
5353- {
5454- input: 'path/to/openapi_one.json',
5555- output: 'src/client_one',
5656- plugins: ['legacy/fetch'],
5757- },
5858- {
5959- input: 'path/to/openapi_two.json',
6060- output: 'src/client_two',
6161- plugins: ['legacy/axios'],
6262- },
6363-])
6464-``` -->
6565-6641## Input
67426843You must provide an input so we can load your OpenAPI specification.
···162137Plugins are responsible for generating artifacts from your input. By default, Hey API will generate TypeScript interfaces and SDK from your OpenAPI specification. You can add, remove, or customize any of the plugins. In fact, we highly encourage you to do so!
163138164139You can learn more on the [Output](/openapi-ts/output) page.
140140+141141+## Advanced
142142+143143+More complex configuration scenarios can be handled by providing an array of inputs, outputs, or configurations.
144144+145145+### Multiple jobs
146146+147147+Throughout this documentation, we generally reference single job configurations. However, you can easily run multiple jobs by providing an array of configuration objects.
148148+149149+::: code-group
150150+151151+```js [config]
152152+export default [
153153+ {
154154+ input: 'foo.yaml',
155155+ output: 'src/foo',
156156+ },
157157+ {
158158+ input: 'bar.yaml',
159159+ output: 'src/bar',
160160+ },
161161+];
162162+```
163163+164164+```md [example]
165165+src/
166166+├── foo/
167167+│ ├── client/
168168+│ ├── core/
169169+│ ├── client.gen.ts
170170+│ ├── index.ts
171171+│ ├── sdk.gen.ts
172172+│ └── types.gen.ts
173173+└── bar/
174174+├── client/
175175+├── core/
176176+├── client.gen.ts
177177+├── index.ts
178178+├── sdk.gen.ts
179179+└── types.gen.ts
180180+```
181181+182182+:::
183183+184184+### Job matrix
185185+186186+Reusing configuration across multiple jobs is possible by defining a job matrix. You can create a job matrix by providing `input` and `output` arrays of matching length.
187187+188188+::: code-group
189189+190190+```js [config]
191191+export default {
192192+ input: ['foo.yaml', 'bar.yaml'],
193193+ output: ['src/foo', 'src/bar'],
194194+};
195195+```
196196+197197+```md [example]
198198+src/
199199+├── foo/
200200+│ ├── client/
201201+│ ├── core/
202202+│ ├── client.gen.ts
203203+│ ├── index.ts
204204+│ ├── sdk.gen.ts
205205+│ └── types.gen.ts
206206+└── bar/
207207+├── client/
208208+├── core/
209209+├── client.gen.ts
210210+├── index.ts
211211+├── sdk.gen.ts
212212+└── types.gen.ts
213213+```
214214+215215+:::
216216+217217+### Merging inputs
218218+219219+You can merge inputs by defining multiple inputs and a single output.
220220+221221+::: code-group
222222+223223+```js [config]
224224+export default {
225225+ input: ['foo.yaml', 'bar.yaml'],
226226+ output: 'src/client',
227227+};
228228+```
229229+230230+```md [example]
231231+src/
232232+└── client/
233233+├── client/
234234+├── core/
235235+├── client.gen.ts
236236+├── index.ts
237237+├── sdk.gen.ts
238238+└── types.gen.ts
239239+```
240240+241241+:::
165242166243## API
167244
+2
docs/openapi-ts/configuration/input.md
···54545555:::
56565757+You can learn more about complex use cases in the [Advanced](/openapi-ts/configuration#advanced) section.
5858+5759::: info
5860If you use an HTTPS URL with a self-signed certificate in development, you will need to set [`NODE_TLS_REJECT_UNAUTHORIZED=0`](https://github.com/hey-api/openapi-ts/issues/276#issuecomment-2043143501) in your environment.
5961:::
+2
docs/openapi-ts/configuration/output.md
···34343535:::
36363737+You can learn more about complex use cases in the [Advanced](/openapi-ts/configuration#advanced) section.
3838+3739## File Name
38403941You can customize the naming and casing pattern for files using the `fileName` option.
···11+import type { MaybeArray } from '../../../types/utils';
12import type { EnumExtensions } from '../../shared/types/openapi-spec-extensions';
23import type { OpenApiSchemaExtensions } from './spec-extensions';
34···144145 /**
145146 * If it is an array, it must be an array of strings, where each string is the name of one of the basic types, and each element is unique. In this case, the JSON snippet is valid if it matches any of the given types.
146147 */
147147- type?: JsonSchemaTypes | ReadonlyArray<JsonSchemaTypes>;
148148+ type?: MaybeArray<JsonSchemaTypes>;
148149 /**
149150 * The boolean keywords `readOnly` and `writeOnly` are typically used in an API context. `readOnly` indicates that a value should not be modified. It could be used to indicate that a `PUT` request that changes a value would result in a `400 Bad Request` response. `writeOnly` indicates that a value may be set, but will remain hidden. In could be used to indicate you can set a value with a `PUT` request, but it would not be included when retrieving that record with a `GET` request.
150151 */
···11import type { PluginConfigMap } from '../plugins/config';
22import type { Plugin, PluginNames } from '../plugins/types';
33-import type { Input, Watch } from './input';
33+import type { Input, UserInput, Watch } from './input';
44import type { Logs } from './logs';
55import type { Output, UserOutput } from './output';
66import type { Parser, UserParser } from './parser';
77+import type { MaybeArray } from './utils';
7889export interface UserConfig {
910 /**
···2728 * object directly if you're fetching the file yourself.
2829 *
2930 * Alternatively, you can define a configuration object with more options.
3131+ *
3232+ * If you define an array, we will generate a single output from multiple
3333+ * inputs. If you define an array of outputs with the same length, we will
3434+ * generate multiple outputs, one for each input.
3035 */
3131- input:
3232- | `https://get.heyapi.dev/${string}/${string}`
3333- | `${string}/${string}`
3434- | `readme:@${string}/${string}#${string}`
3535- | `readme:${string}`
3636- | `scalar:@${string}/${string}`
3737- | (string & {})
3838- | (Record<string, unknown> & { path?: never })
3939- | Input;
3636+ input: MaybeArray<UserInput | Required<UserInput>['path']>;
4037 /**
4138 * Show an interactive error reporting tool when the program crashes? You
4239 * generally want to keep this disabled (default).
···5249 logs?: string | Logs;
5350 /**
5451 * Path to the output folder.
5252+ *
5353+ * If you define an array of outputs with the same length as inputs, we will
5454+ * generate multiple outputs, one for each input.
5555 */
5656- output: string | UserOutput;
5656+ output: MaybeArray<string | UserOutput>;
5757 /**
5858 * Customize how the input is parsed and transformed before it's passed to
5959 * plugins.
···142142 | 'watch'
143143> &
144144 Pick<UserConfig, 'base' | 'name' | 'request'> & {
145145- input: Omit<Input, 'path' | 'watch'> &
146146- Pick<Required<Input>, 'path'> & { watch: Watch };
145145+ /**
146146+ * Path to the input specification.
147147+ */
148148+ input: ReadonlyArray<Input>;
147149 logs: Logs;
150150+ /**
151151+ * Path to the output folder.
152152+ */
148153 output: Output;
149154 /**
150155 * Customize how the input is parsed and transformed before it's passed to
+94-9
packages/openapi-ts/src/types/input.d.ts
···11-export type Input = {
11+type JsonSchema = Record<string, unknown>;
22+33+type ApiRegistryShorthands =
44+ | `https://get.heyapi.dev/${string}/${string}`
55+ | `${string}/${string}`
66+ | `readme:@${string}/${string}#${string}`
77+ | `readme:${string}`
88+ | `scalar:@${string}/${string}`;
99+1010+export type UserInput = {
211 /**
312 * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
413 *
···4453 * Both JSON and YAML file formats are supported. You can also pass the parsed
4554 * object directly if you're fetching the file yourself.
4655 */
4747- path?:
4848- | `https://get.heyapi.dev/${string}/${string}`
4949- | `${string}/${string}`
5050- | `readme:@${string}/${string}#${string}`
5151- | `readme:${string}`
5252- | `scalar:@${string}/${string}`
5353- | (string & {})
5454- | Record<string, unknown>;
5656+ path?: ApiRegistryShorthands | (string & {}) | JsonSchema;
5557 /**
5658 * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
5759 *
···8183 * @default false
8284 */
8385 watch?: boolean | number | Watch;
8686+};
8787+8888+export type Input = {
8989+ /**
9090+ * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
9191+ *
9292+ * Projects are private by default, you will need to be authenticated
9393+ * to download OpenAPI specifications. We recommend using project API
9494+ * keys in CI workflows and personal API keys for local development.
9595+ *
9696+ * API key isn't required for public projects. You can also omit this
9797+ * parameter and provide an environment variable `HEY_API_TOKEN`.
9898+ */
9999+ api_key?: string;
100100+ /**
101101+ * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
102102+ *
103103+ * You can fetch the last build from branch by providing the branch
104104+ * name.
105105+ */
106106+ branch?: string;
107107+ /**
108108+ * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
109109+ *
110110+ * You can fetch an exact specification by providing a commit sha.
111111+ * This will always return the same file.
112112+ */
113113+ commit_sha?: string;
114114+ /**
115115+ * You can pass any valid Fetch API options to the request for fetching your
116116+ * specification. This is useful if your file is behind auth for example.
117117+ */
118118+ fetch?: RequestInit;
119119+ /**
120120+ * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
121121+ *
122122+ * Organization created in Hey API Platform.
123123+ */
124124+ organization?: string;
125125+ /**
126126+ * Path to the OpenAPI specification. This can be:
127127+ * - path
128128+ * - URL
129129+ * - API registry shorthand
130130+ *
131131+ * Both JSON and YAML file formats are supported. You can also pass the parsed
132132+ * object directly if you're fetching the file yourself.
133133+ */
134134+ path: ApiRegistryShorthands | (string & {}) | JsonSchema;
135135+ /**
136136+ * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
137137+ *
138138+ * Project created in Hey API Platform.
139139+ */
140140+ project?: string;
141141+ /**
142142+ * If input path was resolved to a registry, this contains the registry
143143+ * identifier so we don't need to parse it again.
144144+ *
145145+ * @default undefined
146146+ */
147147+ registry?: 'hey-api' | 'readme' | 'scalar';
148148+ /**
149149+ * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
150150+ *
151151+ * If you're tagging your specifications with custom tags, you can use
152152+ * them to filter the results. When you provide multiple tags, only
153153+ * the first match will be returned.
154154+ */
155155+ tags?: ReadonlyArray<string>;
156156+ /**
157157+ * **Requires `path` to start with `https://get.heyapi.dev` or be undefined**
158158+ *
159159+ * Every OpenAPI document contains a required version field. You can
160160+ * use this value to fetch the last uploaded specification matching
161161+ * the value.
162162+ */
163163+ version?: string;
164164+ /**
165165+ * Regenerate the client when the input file changes? You can alternatively
166166+ * pass a numeric value for the interval in ms.
167167+ */
168168+ watch: Watch;
84169};
8517086171export type Watch = {
+28-1
packages/openapi-ts/src/types/utils.d.ts
···11import type { GeneratedFile } from '../generate/file';
2233-/** Recursively make all non-function properties optional */
33+/**
44+ * Converts all top-level ReadonlyArray properties to Array (shallow).
55+ */
66+export type ArrayOnly<T> = {
77+ [K in keyof T]: T[K] extends ReadonlyArray<infer U> ? Array<U> : T[K];
88+};
99+1010+/**
1111+ * Recursively makes all non-function properties optional.
1212+ */
413export type DeepPartial<T> = {
514 [K in keyof T]?: T[K] extends (...args: any[]) => any
615 ? T[K]
···918 : T[K];
1019};
11202121+/** @deprecated */
1222export type Files = Record<string, GeneratedFile>;
2323+2424+/**
2525+ * Accepts a value, a function returning a value, or a function returning a promise of a value.
2626+ */
2727+export type LazyOrAsync<T> = T | (() => T) | (() => Promise<T>);
2828+2929+/**
3030+ * Accepts a value or a readonly array of values of type T.
3131+ */
3232+export type MaybeArray<T> = T | ReadonlyArray<T>;
3333+3434+/**
3535+ * Converts all top-level Array properties to ReadonlyArray (shallow).
3636+ */
3737+export type ReadonlyArrayOnly<T> = {
3838+ [K in keyof T]: T[K] extends Array<infer U> ? ReadonlyArray<U> : T[K];
3939+};