diff --git a/examples/openapi-ts-axios/package.json b/examples/openapi-ts-axios/package.json index 2e8348947..275214da2 100644 --- a/examples/openapi-ts-axios/package.json +++ b/examples/openapi-ts-axios/package.json @@ -1,5 +1,5 @@ { - "name": "@examples/openapi-ts-axios", + "name": "@example/openapi-ts-axios", "private": true, "version": "0.0.0", "type": "module", diff --git a/examples/openapi-ts-axios/src/App.tsx b/examples/openapi-ts-axios/src/App.tsx index f23a3d0be..d44bf9089 100644 --- a/examples/openapi-ts-axios/src/App.tsx +++ b/examples/openapi-ts-axios/src/App.tsx @@ -37,7 +37,7 @@ function App() { alt="Hey API logo" /> -

@hey-api/openapi-ts example

+

@hey-api/openapi-ts 🤝 Axios

+ Fetched pet's name: {pet?.name} +
+
+ {"import { $Pet } from './client/schemas.gen'"} +
{JSON.stringify($Pet, null, 2)}
+
+ + ); +} + +export default App; diff --git a/examples/openapi-ts-fetch/src/client/index.ts b/examples/openapi-ts-fetch/src/client/index.ts new file mode 100644 index 000000000..002afc333 --- /dev/null +++ b/examples/openapi-ts-fetch/src/client/index.ts @@ -0,0 +1,4 @@ +// This file is auto-generated by @hey-api/openapi-ts +export * from './schemas.gen'; +export * from './services.gen'; +export * from './types.gen'; diff --git a/examples/openapi-ts-fetch/src/client/schemas.gen.ts b/examples/openapi-ts-fetch/src/client/schemas.gen.ts new file mode 100644 index 000000000..8f6b9dc92 --- /dev/null +++ b/examples/openapi-ts-fetch/src/client/schemas.gen.ts @@ -0,0 +1,244 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export const $Order = { + properties: { + complete: { + type: 'boolean', + }, + id: { + example: 10, + format: 'int64', + type: 'integer', + }, + petId: { + example: 198772, + format: 'int64', + type: 'integer', + }, + quantity: { + example: 7, + format: 'int32', + type: 'integer', + }, + shipDate: { + format: 'date-time', + type: 'string', + }, + status: { + description: 'Order Status', + enum: ['placed', 'approved', 'delivered'], + example: 'approved', + type: 'string', + }, + }, + type: 'object', + 'x-swagger-router-model': 'io.swagger.petstore.model.Order', + xml: { + name: 'order', + }, +} as const; + +export const $Customer = { + properties: { + address: { + items: { + $ref: '#/components/schemas/Address', + }, + type: 'array', + xml: { + name: 'addresses', + wrapped: true, + }, + }, + id: { + example: 100000, + format: 'int64', + type: 'integer', + }, + username: { + example: 'fehguy', + type: 'string', + }, + }, + type: 'object', + xml: { + name: 'customer', + }, +} as const; + +export const $Address = { + properties: { + city: { + example: 'Palo Alto', + type: 'string', + }, + state: { + example: 'CA', + type: 'string', + }, + street: { + example: '437 Lytton', + type: 'string', + }, + zip: { + example: 94301, + type: 'string', + }, + }, + type: 'object', + xml: { + name: 'address', + }, +} as const; + +export const $Category = { + properties: { + id: { + example: 1, + format: 'int64', + type: 'integer', + }, + name: { + example: 'Dogs', + type: 'string', + }, + }, + type: 'object', + 'x-swagger-router-model': 'io.swagger.petstore.model.Category', + xml: { + name: 'category', + }, +} as const; + +export const $User = { + properties: { + email: { + example: 'john@email.com', + type: 'string', + }, + firstName: { + example: 'John', + type: 'string', + }, + id: { + example: 10, + format: 'int64', + type: 'integer', + }, + lastName: { + example: 'James', + type: 'string', + }, + password: { + example: 12345, + type: 'string', + }, + phone: { + example: 12345, + type: 'string', + }, + userStatus: { + description: 'User Status', + example: 1, + format: 'int32', + type: 'integer', + }, + username: { + example: 'theUser', + type: 'string', + }, + }, + type: 'object', + 'x-swagger-router-model': 'io.swagger.petstore.model.User', + xml: { + name: 'user', + }, +} as const; + +export const $Tag = { + properties: { + id: { + format: 'int64', + type: 'integer', + }, + name: { + type: 'string', + }, + }, + type: 'object', + 'x-swagger-router-model': 'io.swagger.petstore.model.Tag', + xml: { + name: 'tag', + }, +} as const; + +export const $Pet = { + properties: { + category: { + $ref: '#/components/schemas/Category', + }, + id: { + example: 10, + format: 'int64', + type: 'integer', + }, + name: { + example: 'doggie', + type: 'string', + }, + photoUrls: { + items: { + type: 'string', + xml: { + name: 'photoUrl', + }, + }, + type: 'array', + xml: { + wrapped: true, + }, + }, + status: { + description: 'pet status in the store', + enum: ['available', 'pending', 'sold'], + type: 'string', + }, + tags: { + items: { + $ref: '#/components/schemas/Tag', + xml: { + name: 'tag', + }, + }, + type: 'array', + xml: { + wrapped: true, + }, + }, + }, + required: ['name', 'photoUrls'], + type: 'object', + 'x-swagger-router-model': 'io.swagger.petstore.model.Pet', + xml: { + name: 'pet', + }, +} as const; + +export const $ApiResponse = { + properties: { + code: { + format: 'int32', + type: 'integer', + }, + message: { + type: 'string', + }, + type: { + type: 'string', + }, + }, + type: 'object', + xml: { + name: '##default', + }, +} as const; diff --git a/examples/openapi-ts-fetch/src/client/services.gen.ts b/examples/openapi-ts-fetch/src/client/services.gen.ts new file mode 100644 index 000000000..0ba53b385 --- /dev/null +++ b/examples/openapi-ts-fetch/src/client/services.gen.ts @@ -0,0 +1,444 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { CancelablePromise } from '@hey-api/client-fetch'; +import { OpenAPI } from '@hey-api/client-fetch'; +import { request as __request } from '@hey-api/client-fetch'; + +import type { $OpenApiTs } from './types.gen'; + +export class PetService { + /** + * Add a new pet to the store + * Add a new pet to the store + * @param data The data for the request. + * @param data.requestBody Create a new pet in the store + * @returns Pet Successful operation + * @throws ApiError + */ + public static addPet( + data: $OpenApiTs['/pet']['post']['req'], + ): CancelablePromise<$OpenApiTs['/pet']['post']['res'][200]> { + return __request(OpenAPI, { + body: data.requestBody, + errors: { + 405: 'Invalid input', + }, + mediaType: 'application/json', + method: 'POST', + url: '/pet', + }); + } + + /** + * Update an existing pet + * Update an existing pet by Id + * @param data The data for the request. + * @param data.requestBody Update an existent pet in the store + * @returns Pet Successful operation + * @throws ApiError + */ + public static updatePet( + data: $OpenApiTs['/pet']['put']['req'], + ): CancelablePromise<$OpenApiTs['/pet']['put']['res'][200]> { + return __request(OpenAPI, { + body: data.requestBody, + errors: { + 400: 'Invalid ID supplied', + 404: 'Pet not found', + 405: 'Validation exception', + }, + mediaType: 'application/json', + method: 'PUT', + url: '/pet', + }); + } + + /** + * Finds Pets by status + * Multiple status values can be provided with comma separated strings + * @param data The data for the request. + * @param data.status Status values that need to be considered for filter + * @returns Pet successful operation + * @throws ApiError + */ + public static findPetsByStatus( + data: $OpenApiTs['/pet/findByStatus']['get']['req'] = {}, + ): CancelablePromise<$OpenApiTs['/pet/findByStatus']['get']['res'][200]> { + return __request(OpenAPI, { + errors: { + 400: 'Invalid status value', + }, + method: 'GET', + query: { + status: data.status, + }, + url: '/pet/findByStatus', + }); + } + + /** + * Finds Pets by tags + * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + * @param data The data for the request. + * @param data.tags Tags to filter by + * @returns Pet successful operation + * @throws ApiError + */ + public static findPetsByTags( + data: $OpenApiTs['/pet/findByTags']['get']['req'] = {}, + ): CancelablePromise<$OpenApiTs['/pet/findByTags']['get']['res'][200]> { + return __request(OpenAPI, { + errors: { + 400: 'Invalid tag value', + }, + method: 'GET', + query: { + tags: data.tags, + }, + url: '/pet/findByTags', + }); + } + + /** + * Find pet by ID + * Returns a single pet + * @param data The data for the request. + * @param data.petId ID of pet to return + * @returns Pet successful operation + * @throws ApiError + */ + public static getPetById( + data: $OpenApiTs['/pet/{petId}']['get']['req'], + ): CancelablePromise<$OpenApiTs['/pet/{petId}']['get']['res'][200]> { + return __request(OpenAPI, { + errors: { + 400: 'Invalid ID supplied', + 404: 'Pet not found', + }, + method: 'GET', + path: { + petId: data.petId, + }, + url: '/pet/{petId}', + }); + } + + /** + * Updates a pet in the store with form data + * @param data The data for the request. + * @param data.petId ID of pet that needs to be updated + * @param data.name Name of pet that needs to be updated + * @param data.status Status of pet that needs to be updated + * @throws ApiError + */ + public static updatePetWithForm( + data: $OpenApiTs['/pet/{petId}']['post']['req'], + ): CancelablePromise { + return __request(OpenAPI, { + errors: { + 405: 'Invalid input', + }, + method: 'POST', + path: { + petId: data.petId, + }, + query: { + name: data.name, + status: data.status, + }, + url: '/pet/{petId}', + }); + } + + /** + * Deletes a pet + * @param data The data for the request. + * @param data.petId Pet id to delete + * @param data.apiKey + * @throws ApiError + */ + public static deletePet( + data: $OpenApiTs['/pet/{petId}']['delete']['req'], + ): CancelablePromise { + return __request(OpenAPI, { + errors: { + 400: 'Invalid pet value', + }, + headers: { + api_key: data.apiKey, + }, + method: 'DELETE', + path: { + petId: data.petId, + }, + url: '/pet/{petId}', + }); + } + + /** + * uploads an image + * @param data The data for the request. + * @param data.petId ID of pet to update + * @param data.additionalMetadata Additional Metadata + * @param data.requestBody + * @returns ApiResponse successful operation + * @throws ApiError + */ + public static uploadFile( + data: $OpenApiTs['/pet/{petId}/uploadImage']['post']['req'], + ): CancelablePromise< + $OpenApiTs['/pet/{petId}/uploadImage']['post']['res'][200] + > { + return __request(OpenAPI, { + body: data.requestBody, + mediaType: 'application/octet-stream', + method: 'POST', + path: { + petId: data.petId, + }, + query: { + additionalMetadata: data.additionalMetadata, + }, + url: '/pet/{petId}/uploadImage', + }); + } +} + +export class StoreService { + /** + * Returns pet inventories by status + * Returns a map of status codes to quantities + * @returns number successful operation + * @throws ApiError + */ + public static getInventory(): CancelablePromise< + $OpenApiTs['/store/inventory']['get']['res'][200] + > { + return __request(OpenAPI, { + method: 'GET', + url: '/store/inventory', + }); + } + + /** + * Place an order for a pet + * Place a new order in the store + * @param data The data for the request. + * @param data.requestBody + * @returns Order successful operation + * @throws ApiError + */ + public static placeOrder( + data: $OpenApiTs['/store/order']['post']['req'] = {}, + ): CancelablePromise<$OpenApiTs['/store/order']['post']['res'][200]> { + return __request(OpenAPI, { + body: data.requestBody, + errors: { + 405: 'Invalid input', + }, + mediaType: 'application/json', + method: 'POST', + url: '/store/order', + }); + } + + /** + * Find purchase order by ID + * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions. + * @param data The data for the request. + * @param data.orderId ID of order that needs to be fetched + * @returns Order successful operation + * @throws ApiError + */ + public static getOrderById( + data: $OpenApiTs['/store/order/{orderId}']['get']['req'], + ): CancelablePromise< + $OpenApiTs['/store/order/{orderId}']['get']['res'][200] + > { + return __request(OpenAPI, { + errors: { + 400: 'Invalid ID supplied', + 404: 'Order not found', + }, + method: 'GET', + path: { + orderId: data.orderId, + }, + url: '/store/order/{orderId}', + }); + } + + /** + * Delete purchase order by ID + * For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + * @param data The data for the request. + * @param data.orderId ID of the order that needs to be deleted + * @throws ApiError + */ + public static deleteOrder( + data: $OpenApiTs['/store/order/{orderId}']['delete']['req'], + ): CancelablePromise { + return __request(OpenAPI, { + errors: { + 400: 'Invalid ID supplied', + 404: 'Order not found', + }, + method: 'DELETE', + path: { + orderId: data.orderId, + }, + url: '/store/order/{orderId}', + }); + } +} + +export class UserService { + /** + * Create user + * This can only be done by the logged in user. + * @param data The data for the request. + * @param data.requestBody Created user object + * @returns User successful operation + * @throws ApiError + */ + public static createUser( + data: $OpenApiTs['/user']['post']['req'] = {}, + ): CancelablePromise<$OpenApiTs['/user']['post']['res'][200]> { + return __request(OpenAPI, { + body: data.requestBody, + mediaType: 'application/json', + method: 'POST', + url: '/user', + }); + } + + /** + * Creates list of users with given input array + * Creates list of users with given input array + * @param data The data for the request. + * @param data.requestBody + * @returns User Successful operation + * @returns unknown successful operation + * @throws ApiError + */ + public static createUsersWithListInput( + data: $OpenApiTs['/user/createWithList']['post']['req'] = {}, + ): CancelablePromise< + | $OpenApiTs['/user/createWithList']['post']['res'][200] + | $OpenApiTs['/user/createWithList']['post']['res'][200] + > { + return __request(OpenAPI, { + body: data.requestBody, + mediaType: 'application/json', + method: 'POST', + url: '/user/createWithList', + }); + } + + /** + * Logs user into the system + * @param data The data for the request. + * @param data.username The user name for login + * @param data.password The password for login in clear text + * @returns string successful operation + * @throws ApiError + */ + public static loginUser( + data: $OpenApiTs['/user/login']['get']['req'] = {}, + ): CancelablePromise<$OpenApiTs['/user/login']['get']['res'][200]> { + return __request(OpenAPI, { + errors: { + 400: 'Invalid username/password supplied', + }, + method: 'GET', + query: { + password: data.password, + username: data.username, + }, + url: '/user/login', + }); + } + + /** + * Logs out current logged in user session + * @returns unknown successful operation + * @throws ApiError + */ + public static logoutUser(): CancelablePromise< + $OpenApiTs['/user/logout']['get']['res'][200] + > { + return __request(OpenAPI, { + method: 'GET', + url: '/user/logout', + }); + } + + /** + * Get user by user name + * @param data The data for the request. + * @param data.username The name that needs to be fetched. Use user1 for testing. + * @returns User successful operation + * @throws ApiError + */ + public static getUserByName( + data: $OpenApiTs['/user/{username}']['get']['req'], + ): CancelablePromise<$OpenApiTs['/user/{username}']['get']['res'][200]> { + return __request(OpenAPI, { + errors: { + 400: 'Invalid username supplied', + 404: 'User not found', + }, + method: 'GET', + path: { + username: data.username, + }, + url: '/user/{username}', + }); + } + + /** + * Update user + * This can only be done by the logged in user. + * @param data The data for the request. + * @param data.username name that needs to be updated + * @param data.requestBody Update an existent user in the store + * @returns unknown successful operation + * @throws ApiError + */ + public static updateUser( + data: $OpenApiTs['/user/{username}']['put']['req'], + ): CancelablePromise<$OpenApiTs['/user/{username}']['put']['res'][200]> { + return __request(OpenAPI, { + body: data.requestBody, + mediaType: 'application/json', + method: 'PUT', + path: { + username: data.username, + }, + url: '/user/{username}', + }); + } + + /** + * Delete user + * This can only be done by the logged in user. + * @param data The data for the request. + * @param data.username The name that needs to be deleted + * @throws ApiError + */ + public static deleteUser( + data: $OpenApiTs['/user/{username}']['delete']['req'], + ): CancelablePromise { + return __request(OpenAPI, { + errors: { + 400: 'Invalid username supplied', + 404: 'User not found', + }, + method: 'DELETE', + path: { + username: data.username, + }, + url: '/user/{username}', + }); + } +} diff --git a/examples/openapi-ts-fetch/src/client/types.gen.ts b/examples/openapi-ts-fetch/src/client/types.gen.ts new file mode 100644 index 000000000..3e81f631b --- /dev/null +++ b/examples/openapi-ts-fetch/src/client/types.gen.ts @@ -0,0 +1,433 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type Order = { + id?: number; + petId?: number; + quantity?: number; + shipDate?: string; + /** + * Order Status + */ + status?: 'placed' | 'approved' | 'delivered'; + complete?: boolean; +}; + +export type Customer = { + id?: number; + username?: string; + address?: Array
; +}; + +export type Address = { + street?: string; + city?: string; + state?: string; + zip?: string; +}; + +export type Category = { + id?: number; + name?: string; +}; + +export type User = { + id?: number; + username?: string; + firstName?: string; + lastName?: string; + email?: string; + password?: string; + phone?: string; + /** + * User Status + */ + userStatus?: number; +}; + +export type Tag = { + id?: number; + name?: string; +}; + +export type Pet = { + id?: number; + name: string; + category?: Category; + photoUrls: Array; + tags?: Array; + /** + * pet status in the store + */ + status?: 'available' | 'pending' | 'sold'; +}; + +export type ApiResponse = { + code?: number; + type?: string; + message?: string; +}; + +export type $OpenApiTs = { + '/pet': { + post: { + req: { + /** + * Create a new pet in the store + */ + requestBody: Pet; + }; + res: { + /** + * Successful operation + */ + 200: Pet; + /** + * Invalid input + */ + 405: unknown; + }; + }; + put: { + req: { + /** + * Update an existent pet in the store + */ + requestBody: Pet; + }; + res: { + /** + * Successful operation + */ + 200: Pet; + /** + * Invalid ID supplied + */ + 400: unknown; + /** + * Pet not found + */ + 404: unknown; + /** + * Validation exception + */ + 405: unknown; + }; + }; + }; + '/pet/findByStatus': { + get: { + req: { + /** + * Status values that need to be considered for filter + */ + status?: 'available' | 'pending' | 'sold'; + }; + res: { + /** + * successful operation + */ + 200: Array; + /** + * Invalid status value + */ + 400: unknown; + }; + }; + }; + '/pet/findByTags': { + get: { + req: { + /** + * Tags to filter by + */ + tags?: Array; + }; + res: { + /** + * successful operation + */ + 200: Array; + /** + * Invalid tag value + */ + 400: unknown; + }; + }; + }; + '/pet/{petId}': { + get: { + req: { + /** + * ID of pet to return + */ + petId: number; + }; + res: { + /** + * successful operation + */ + 200: Pet; + /** + * Invalid ID supplied + */ + 400: unknown; + /** + * Pet not found + */ + 404: unknown; + }; + }; + post: { + req: { + /** + * Name of pet that needs to be updated + */ + name?: string; + /** + * ID of pet that needs to be updated + */ + petId: number; + /** + * Status of pet that needs to be updated + */ + status?: string; + }; + res: { + /** + * Invalid input + */ + 405: unknown; + }; + }; + delete: { + req: { + apiKey?: string; + /** + * Pet id to delete + */ + petId: number; + }; + res: { + /** + * Invalid pet value + */ + 400: unknown; + }; + }; + }; + '/pet/{petId}/uploadImage': { + post: { + req: { + /** + * Additional Metadata + */ + additionalMetadata?: string; + /** + * ID of pet to update + */ + petId: number; + requestBody?: Blob | File; + }; + res: { + /** + * successful operation + */ + 200: ApiResponse; + }; + }; + }; + '/store/inventory': { + get: { + res: { + /** + * successful operation + */ + 200: { + [key: string]: number; + }; + }; + }; + }; + '/store/order': { + post: { + req: { + requestBody?: Order; + }; + res: { + /** + * successful operation + */ + 200: Order; + /** + * Invalid input + */ + 405: unknown; + }; + }; + }; + '/store/order/{orderId}': { + get: { + req: { + /** + * ID of order that needs to be fetched + */ + orderId: number; + }; + res: { + /** + * successful operation + */ + 200: Order; + /** + * Invalid ID supplied + */ + 400: unknown; + /** + * Order not found + */ + 404: unknown; + }; + }; + delete: { + req: { + /** + * ID of the order that needs to be deleted + */ + orderId: number; + }; + res: { + /** + * Invalid ID supplied + */ + 400: unknown; + /** + * Order not found + */ + 404: unknown; + }; + }; + }; + '/user': { + post: { + req: { + /** + * Created user object + */ + requestBody?: User; + }; + res: { + /** + * successful operation + */ + 200: User; + }; + }; + }; + '/user/createWithList': { + post: { + req: { + requestBody?: Array; + }; + res: { + /** + * successful operation + */ + 200: unknown; + }; + }; + }; + '/user/login': { + get: { + req: { + /** + * The password for login in clear text + */ + password?: string; + /** + * The user name for login + */ + username?: string; + }; + res: { + /** + * successful operation + */ + 200: string; + /** + * Invalid username/password supplied + */ + 400: unknown; + }; + }; + }; + '/user/logout': { + get: { + res: { + /** + * successful operation + */ + 200: unknown; + }; + }; + }; + '/user/{username}': { + get: { + req: { + /** + * The name that needs to be fetched. Use user1 for testing. + */ + username: string; + }; + res: { + /** + * successful operation + */ + 200: User; + /** + * Invalid username supplied + */ + 400: unknown; + /** + * User not found + */ + 404: unknown; + }; + }; + put: { + req: { + /** + * Update an existent user in the store + */ + requestBody?: User; + /** + * name that needs to be updated + */ + username: string; + }; + res: { + /** + * successful operation + */ + 200: unknown; + }; + }; + delete: { + req: { + /** + * The name that needs to be deleted + */ + username: string; + }; + res: { + /** + * Invalid username supplied + */ + 400: unknown; + /** + * User not found + */ + 404: unknown; + }; + }; + }; +}; diff --git a/examples/openapi-ts-fetch/src/index.css b/examples/openapi-ts-fetch/src/index.css new file mode 100644 index 000000000..6119ad9a8 --- /dev/null +++ b/examples/openapi-ts-fetch/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/openapi-ts-fetch/src/main.tsx b/examples/openapi-ts-fetch/src/main.tsx new file mode 100644 index 000000000..ef88ac07b --- /dev/null +++ b/examples/openapi-ts-fetch/src/main.tsx @@ -0,0 +1,12 @@ +import './index.css'; + +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import App from './App.tsx'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/examples/openapi-ts-fetch/src/vite-env.d.ts b/examples/openapi-ts-fetch/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/examples/openapi-ts-fetch/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/openapi-ts-fetch/tsconfig.json b/examples/openapi-ts-fetch/tsconfig.json new file mode 100644 index 000000000..a7fc6fbf2 --- /dev/null +++ b/examples/openapi-ts-fetch/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/openapi-ts-fetch/tsconfig.node.json b/examples/openapi-ts-fetch/tsconfig.node.json new file mode 100644 index 000000000..97ede7ee6 --- /dev/null +++ b/examples/openapi-ts-fetch/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/openapi-ts-fetch/vite.config.ts b/examples/openapi-ts-fetch/vite.config.ts new file mode 100644 index 000000000..4e7004ebc --- /dev/null +++ b/examples/openapi-ts-fetch/vite.config.ts @@ -0,0 +1,7 @@ +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}); diff --git a/package.json b/package.json index c78c6b4ac..95b5d2976 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,9 @@ "scripts": { "build": "pnpm --recursive build", "changeset": "changeset", - "client-axios": "pnpm --filter @hey-api/client-axios --", - "client-core": "pnpm --filter @hey-api/client-core --", - "client-fetch": "pnpm --filter @hey-api/client-fetch --", - "client-nextjs": "pnpm --filter @hey-api/client-nextjs --", + "client": "sh ./scripts/client.sh", "docs": "pnpm --filter @hey-api/docs --", - "example": "pnpm --filter @examples/openapi-ts-axios --", + "example": "sh ./scripts/example.sh", "format": "prettier --write .", "lint:fix": "prettier --check . && eslint . --fix", "lint": "prettier --check . && eslint .", diff --git a/packages/client-fetch/package.json b/packages/client-fetch/package.json index d45add52a..4318a7ba7 100644 --- a/packages/client-fetch/package.json +++ b/packages/client-fetch/package.json @@ -26,13 +26,16 @@ "typescript", "vue" ], - "main": "./dist/index.cjs", - "types": "./dist/index.d.ts", + "exports": { + "import": "./dist/node/index.mjs", + "require": "./dist/node/index.cjs", + "types": "./dist/node/index.d.ts" + }, "scripts": { "build-bundle": "rollup --config rollup.config.ts --configPlugin typescript", "build-types-check": "tsc --project tsconfig.check.json", "build-types-roll": "rollup --config rollup.dts.config.ts --configPlugin typescript && rimraf temp", - "build-types-temp": "tsc --emitDeclarationOnly --outDir temp -p src", + "build-types-temp": "tsc --emitDeclarationOnly --outDir temp -p src/node", "build-types": "pnpm build-types-temp && pnpm build-types-roll && pnpm build-types-check", "build": "pnpm clean && pnpm build-bundle && pnpm build-types", "clean": "rimraf dist coverage node_modules/.cache", diff --git a/packages/client-fetch/rollup.config.ts b/packages/client-fetch/rollup.config.ts index d5cbd4f68..579f86706 100644 --- a/packages/client-fetch/rollup.config.ts +++ b/packages/client-fetch/rollup.config.ts @@ -20,28 +20,48 @@ export const externalDependencies = [ ]; function createConfig(isProduction: boolean) { - return defineConfig({ - external: externalDependencies, - input: path.resolve(__dirname, 'src/index.ts'), - output: { - file: path.resolve(__dirname, 'dist/index.cjs'), - format: 'cjs', - }, - plugins: [ - typescript({ - declaration: false, - tsconfig: path.resolve(__dirname, 'src/tsconfig.json'), - }), - commonjs({ - sourceMap: false, - }), - isProduction && terser(), - ], - }); + return [ + defineConfig({ + external: externalDependencies, + input: path.resolve(__dirname, 'src/node/index.ts'), + output: { + file: path.resolve(__dirname, 'dist/node/index.cjs'), + format: 'cjs', + }, + plugins: [ + typescript({ + declaration: false, + tsconfig: path.resolve(__dirname, 'src/node/tsconfig.json'), + }), + commonjs({ + sourceMap: false, + }), + isProduction && terser(), + ], + }), + defineConfig({ + external: externalDependencies, + input: path.resolve(__dirname, 'src/node/index.ts'), + output: { + file: path.resolve(__dirname, 'dist/node/index.mjs'), + format: 'esm', + }, + plugins: [ + typescript({ + declaration: false, + tsconfig: path.resolve(__dirname, 'src/node/tsconfig.json'), + }), + commonjs({ + sourceMap: false, + }), + isProduction && terser(), + ], + }), + ]; } export default (commandLineArgs: any): RollupOptions[] => { const isDev = commandLineArgs.watch; const isProduction = !isDev; - return defineConfig([createConfig(isProduction)]); + return defineConfig(createConfig(isProduction)); }; diff --git a/packages/client-fetch/rollup.dts.config.ts b/packages/client-fetch/rollup.dts.config.ts index 8a04de3c0..ebb285d54 100644 --- a/packages/client-fetch/rollup.dts.config.ts +++ b/packages/client-fetch/rollup.dts.config.ts @@ -6,10 +6,10 @@ import { externalDependencies } from './rollup.config'; export default defineConfig({ external: externalDependencies, input: { - index: './temp/index.d.ts', + index: './temp/node/index.d.ts', }, output: { - dir: './dist', + dir: './dist/node', format: 'cjs', }, plugins: [dts({ respectExternal: true })], diff --git a/packages/client-fetch/src/index.ts b/packages/client-fetch/src/index.ts index 7804a5dfe..ae75152a2 100644 --- a/packages/client-fetch/src/index.ts +++ b/packages/client-fetch/src/index.ts @@ -17,6 +17,40 @@ import { resolve, } from '@hey-api/client-core'; +type Middleware = (value: T) => T | Promise; + +class Interceptors { + _fns: Middleware[]; + + constructor() { + this._fns = []; + } + + eject(fn: Middleware) { + const index = this._fns.indexOf(fn); + if (index !== -1) { + this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; + } + } + + use(fn: Middleware) { + this._fns = [...this._fns, fn]; + } +} + +export const OpenAPI: OpenAPIConfig = { + BASE: '', + CREDENTIALS: 'include', + ENCODE_PATH: undefined, + HEADERS: undefined, + PASSWORD: undefined, + TOKEN: undefined, + USERNAME: undefined, + VERSION: '1.26.0', + WITH_CREDENTIALS: false, + interceptors: { request: new Interceptors(), response: new Interceptors() }, +}; + export const getHeaders = async ( config: OpenAPIConfig, options: ApiRequestOptions, diff --git a/packages/client-fetch/src/node/index.ts b/packages/client-fetch/src/node/index.ts new file mode 100644 index 000000000..5cfff654a --- /dev/null +++ b/packages/client-fetch/src/node/index.ts @@ -0,0 +1,2 @@ +export { OpenAPI, request } from '../'; +export type { CancelablePromise } from '@hey-api/client-core'; diff --git a/packages/client-fetch/src/node/tsconfig.json b/packages/client-fetch/src/node/tsconfig.json new file mode 100644 index 000000000..7a9b791b8 --- /dev/null +++ b/packages/client-fetch/src/node/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "lib": ["ES2020", "DOM"], + "stripInternal": true + }, + "exclude": ["../**/__tests__"], + "extends": "../../tsconfig.base.json", + "include": ["../"] +} diff --git a/packages/client-fetch/src/tsconfig.json b/packages/client-fetch/src/tsconfig.json deleted file mode 100644 index 4928e973b..000000000 --- a/packages/client-fetch/src/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["ESNext"], - "stripInternal": true - }, - "exclude": ["./**/__tests__"], - "extends": "../tsconfig.base.json", - "include": ["./"] -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ec049599..a83e4a02f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,6 +134,55 @@ importers: specifier: 5.2.10 version: 5.2.10(@types/node@20.12.7) + examples/openapi-ts-fetch: + dependencies: + '@hey-api/client-fetch': + specifier: workspace:* + version: link:../../packages/client-fetch + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + devDependencies: + '@hey-api/openapi-ts': + specifier: workspace:* + version: link:../../packages/openapi-ts + '@types/react': + specifier: 18.2.79 + version: 18.2.79 + '@types/react-dom': + specifier: 18.2.25 + version: 18.2.25 + '@typescript-eslint/eslint-plugin': + specifier: 7.7.1 + version: 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/parser': + specifier: 7.7.1 + version: 7.7.1(eslint@8.57.0)(typescript@5.4.5) + '@vitejs/plugin-react': + specifier: 4.2.1 + version: 4.2.1(vite@5.2.10) + eslint: + specifier: 8.57.0 + version: 8.57.0 + eslint-plugin-react-hooks: + specifier: 4.6.0 + version: 4.6.0(eslint@8.57.0) + eslint-plugin-react-refresh: + specifier: 0.4.6 + version: 0.4.6(eslint@8.57.0) + prettier: + specifier: 3.2.5 + version: 3.2.5 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vite: + specifier: 5.2.10 + version: 5.2.10(@types/node@20.12.7) + packages/client-axios: dependencies: '@hey-api/client-core': diff --git a/scripts/client.sh b/scripts/client.sh new file mode 100644 index 000000000..15b7cc34a --- /dev/null +++ b/scripts/client.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +pnpm --filter @hey-api/client-$1 -- $2 diff --git a/scripts/example.sh b/scripts/example.sh new file mode 100644 index 000000000..622dacd55 --- /dev/null +++ b/scripts/example.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +pnpm --filter @example/openapi-ts-$1 -- $2