diff --git a/dev/package.json b/dev/package.json index f6b10b4a8f..1889650cf3 100644 --- a/dev/package.json +++ b/dev/package.json @@ -21,6 +21,7 @@ "@tanstack/svelte-query": "5.90.2", "@tanstack/vue-query": "5.92.9", "arktype": "2.2.0", + "msw": "2.10.2", "nuxt": "3.21.0", "swr": "2.4.1", "tsx": "4.21.0", diff --git a/dev/typescript/presets.ts b/dev/typescript/presets.ts index 1381fc53d4..d6eb27c44b 100644 --- a/dev/typescript/presets.ts +++ b/dev/typescript/presets.ts @@ -28,6 +28,7 @@ export const presets = { }, }, ], + msw: () => ['@hey-api/typescript', 'msw'], sdk: () => [ /** SDK with types */ '@hey-api/typescript', diff --git a/docs/openapi-ts/plugins/msw.md b/docs/openapi-ts/plugins/msw.md index 56decd8c05..8aaa266736 100644 --- a/docs/openapi-ts/plugins/msw.md +++ b/docs/openapi-ts/plugins/msw.md @@ -4,15 +4,226 @@ description: MSW plugin for Hey API. Compatible with all our features. --- -# MSW soon - - +# MSW ### About [MSW](https://mswjs.io) is an API mocking library that allows you to write client-agnostic mocks and reuse them across any frameworks, tools, and environments. +The MSW plugin for Hey API generates type-safe mock handler factories from your OpenAPI spec, removing the tedious work of defining mock endpoints and ensuring your mocks stay in sync with your API. + +## Features + +- type-safe mock handlers generated from your OpenAPI spec +- seamless integration with `@hey-api/openapi-ts` ecosystem +- support for static response values or custom MSW resolver functions +- `ofAll` helper to generate handlers for every operation at once +- typed path parameters and request bodies +- automatic base URL inference from OpenAPI `servers` field +- minimal learning curve thanks to extending the underlying technology + +## Installation + +::: warning +MSW plugin requires `msw@^2` as a peer dependency. Make sure to install it in your project. +::: + +In your [configuration](/openapi-ts/get-started), add `msw` to your plugins and you'll be ready to generate MSW artifacts. :tada: + +```js +export default { + // ...other options + plugins: [ + // ...other plugins + 'msw', // [!code ++] + ], +}; +``` + +## Output + +The MSW plugin will generate a `msw.gen.ts` file containing the following artifacts. + +### Handler Factory + +The `createMswHandlerFactory` function is the main export. It returns a `MswHandlerFactory` object with an `of` property containing a handler creator for each operation in your spec, and an `ofAll` method to generate handlers for all operations at once. + +```ts +import { createMswHandlerFactory } from './client/msw.gen'; + +const createMock = createMswHandlerFactory({ + baseUrl: 'http://localhost:8000', // optional, inferred from spec servers +}); +``` + +If your OpenAPI spec defines a `servers` field, the base URL will be inferred automatically. You can override it by passing `baseUrl` in the configuration. + +### Handler Creators + +Each operation becomes a handler creator on the `of` object. Handler creators accept either a static response object with `status` and `result` properties, or a custom MSW `HttpResponseResolver` function. All handler creators also accept an optional `options` parameter of type `RequestHandlerOptions` from MSW. + +## Usage + +### Static Response + +The simplest way to mock an endpoint is to provide a static response object with `status` and `result` properties. Both properties are type-checked against the operation's response types. + +```ts +import { setupServer } from 'msw/node'; +import { createMswHandlerFactory } from './client/msw.gen'; + +const createMock = createMswHandlerFactory(); + +const mockServer = setupServer( + // provide a static response with status code and result + createMock.of.getPetById({ status: 200, result: { id: 1, name: 'Fido' } }), + + // type error if status or result type is incorrect + // @ts-expect-error + createMock.of.getPetById({ status: 200, result: 'wrong type' }), +); +``` + +### Custom Resolver + +For more control, pass an MSW `HttpResponseResolver` function. The resolver receives typed path parameters and request body when available. + +```ts +import { delay, HttpResponse } from 'msw'; +import { setupServer } from 'msw/node'; +import { createMswHandlerFactory } from './client/msw.gen'; + +const createMock = createMswHandlerFactory(); + +const mockServer = setupServer( + // custom resolver with typed params and body + createMock.of.updatePet(async ({ request, params }) => { + const body = await request.json(); + return HttpResponse.json({ id: Number(params.petId), ...body }, { status: 200 }); + }), + + // async resolver with delay + createMock.of.getPetById(async () => { + await delay(100); + return HttpResponse.json({ id: 1, name: 'Fido' }); + }), +); +``` + +::: tip +Path parameters are typed as `string | ReadonlyArray` because MSW normalizes all path parameters to strings. Use `Number()` or similar conversions if you need numeric values. +::: + +### Operations Without Responses + +For operations that don't define a response type, the handler creator can be invoked without arguments or with a custom resolver function. + +```ts +const mockServer = setupServer( + createMock.of.deletePet(), + + createMock.of.deletePet(() => new HttpResponse(null, { status: 204 })), +); +``` + +### Response Examples + +When your OpenAPI spec includes response examples, the generated handlers will use them as default values. This means you can call the handler without arguments and it will return the example response automatically. + +```ts +const mockServer = setupServer( + // uses the example response from the OpenAPI spec as default + createMock.of.getFoo(), + + // you can still override with a custom response + createMock.of.getFoo({ status: 200, result: { name: 'Custom' } }), +); +``` + +By default, `valueSources` is set to `['example']`, which embeds OpenAPI examples in the generated output. To disable this, set `valueSources` to an empty array. + +::: code-group + +```js [config] +export default { + // ...other options + plugins: [ + // ...other plugins + { + name: 'msw', + valueSources: [], // [!code ++] + }, + ], +}; +``` + +::: + +### All Handlers (`ofAll`) + +The `ofAll` method generates handlers for all operations at once. This is useful for quickly setting up a mock server without manually listing each operation. + +```ts +import { setupServer } from 'msw/node'; +import { createMswHandlerFactory } from './client/msw.gen'; + +const createMock = createMswHandlerFactory(); + +const server = setupServer(...createMock.ofAll()); +``` + +#### `onMissingMock` + +Some operations require a response argument (because they have no default example value). The `onMissingMock` option controls what happens for these operations: + +- `'skip'` (default) — skips handlers that require an argument, only including operations that have default values or no response type +- `'error'` — includes all handlers, but operations without a provided response return a `501` error with the message `'[heyapi-msw] The mock of this request is not implemented.'` + +```ts +// strict mode: all endpoints are mocked, missing ones return 501 +const server = setupServer(...createMock.ofAll({ onMissingMock: 'error' })); +``` + +#### `overrides` + +Use `overrides` to provide static responses for specific operations. The keys are operation IDs and the values are the same as what you'd pass to the individual `of` handler creator. + +```ts +const server = setupServer( + ...createMock.ofAll({ + onMissingMock: 'skip', + overrides: { + getPetById: { + status: 200, + result: { id: 1, name: 'Fido', photoUrls: [] }, + }, + }, + }), +); +``` + +When an override is provided for a required handler, it will always be included regardless of the `onMissingMock` setting. + +### Handler Options + +[Handler options](https://mswjs.io/docs/api/http#handler-options) can be provided. The object will be passed on to MSW helpers. + +```ts +const mockServer = setupServer( + createMock.of.getPetById({ status: 200, result: { id: 1, name: 'Fido' } }, { once: true }), +); +``` + +## Known Limitations + +- Query parameters are not typed in the resolver. MSW doesn't provide typed query params natively — use `new URL(request.url).searchParams` instead. +- The response type generic is omitted from `HttpResponseResolver` to avoid MSW's `DefaultBodyType` constraint issues with union and void response types. + +## API + +You can view the complete list of options in the [UserConfig](https://github.com/hey-api/openapi-ts/blob/main/packages/openapi-ts/src/plugins/msw/types.ts) interface. + + diff --git a/examples/openapi-ts-fetch/openapi-ts.config.ts b/examples/openapi-ts-fetch/openapi-ts.config.ts index acb2c6549c..535ba57340 100644 --- a/examples/openapi-ts-fetch/openapi-ts.config.ts +++ b/examples/openapi-ts-fetch/openapi-ts.config.ts @@ -18,5 +18,6 @@ export default defineConfig({ enums: 'javascript', name: '@hey-api/typescript', }, + 'msw', ], }); diff --git a/examples/openapi-ts-fetch/package.json b/examples/openapi-ts-fetch/package.json index 165a1e265a..b95dacc250 100644 --- a/examples/openapi-ts-fetch/package.json +++ b/examples/openapi-ts-fetch/package.json @@ -9,6 +9,7 @@ "lint": "eslint . --report-unused-disable-directives --max-warnings 0", "openapi-ts": "openapi-ts", "preview": "vite preview", + "test": "vitest run", "typecheck": "tsgo --noEmit" }, "dependencies": { @@ -29,10 +30,12 @@ "eslint": "9.17.0", "eslint-plugin-react-hooks": "5.2.0", "eslint-plugin-react-refresh": "0.4.7", + "msw": "2.10.2", "oxfmt": "0.27.0", "postcss": "8.4.41", "tailwindcss": "3.4.9", "typescript": "5.9.3", - "vite": "7.3.1" + "vite": "7.3.1", + "vitest": "4.0.18" } } diff --git a/examples/openapi-ts-fetch/src/client/msw.gen.ts b/examples/openapi-ts-fetch/src/client/msw.gen.ts new file mode 100644 index 0000000000..2af63c4678 --- /dev/null +++ b/examples/openapi-ts-fetch/src/client/msw.gen.ts @@ -0,0 +1,396 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { + http, + type HttpHandler, + HttpResponse, + type HttpResponseResolver, + type RequestHandlerOptions, +} from 'msw'; + +import type { + AddPetData, + AddPetResponses, + CreateUserData, + CreateUserResponses, + CreateUsersWithListInputData, + CreateUsersWithListInputResponses, + DeleteOrderData, + DeleteOrderResponses, + DeletePetData, + DeletePetResponses, + DeleteUserData, + DeleteUserResponses, + FindPetsByStatusResponses, + FindPetsByTagsResponses, + GetInventoryResponses, + GetOrderByIdData, + GetOrderByIdResponses, + GetPetByIdData, + GetPetByIdResponses, + GetUserByNameData, + GetUserByNameResponses, + LoginUserResponses, + LogoutUserResponses, + PlaceOrderData, + PlaceOrderResponses, + UpdatePetData, + UpdatePetResponses, + UpdatePetWithFormData, + UpdatePetWithFormResponses, + UpdateUserData, + UpdateUserResponses, + UploadFileData, + UploadFileResponses, +} from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => + `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + result: T[K]; + status: K; + }; +}[Extract]; + +type HttpHandlerFactory = ( + responseOrResolver: ResponseOrResolver, + options?: RequestHandlerOptions, +) => HttpHandler; + +type StringifyPathParams = { + [K in keyof T]-?: string | ReadonlyArray; +}; + +type OptionalHttpHandlerFactory = ( + responseOrResolver?: ResponseOrResolver, + options?: RequestHandlerOptions, +) => HttpHandler; + +export type SingleHandlerFactories = { + addPet: HttpHandlerFactory< + ToResponseUnion | HttpResponseResolver + >; + createUser: HttpHandlerFactory< + ToResponseUnion | HttpResponseResolver + >; + createUsersWithListInput: HttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver + >; + deleteOrder: OptionalHttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver>, never> + >; + deletePet: OptionalHttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver>, never> + >; + deleteUser: OptionalHttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver>, never> + >; + findPetsByStatus: HttpHandlerFactory< + ToResponseUnion | HttpResponseResolver + >; + findPetsByTags: HttpHandlerFactory< + ToResponseUnion | HttpResponseResolver + >; + getInventory: HttpHandlerFactory | HttpResponseResolver>; + getOrderById: HttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver>, never> + >; + getPetById: HttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver>, never> + >; + getUserByName: HttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver>, never> + >; + loginUser: HttpHandlerFactory | HttpResponseResolver>; + logoutUser: OptionalHttpHandlerFactory< + ToResponseUnion | HttpResponseResolver + >; + placeOrder: HttpHandlerFactory< + ToResponseUnion | HttpResponseResolver + >; + updatePet: HttpHandlerFactory< + ToResponseUnion | HttpResponseResolver + >; + updatePetWithForm: HttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver>, never> + >; + updateUser: OptionalHttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver< + StringifyPathParams>, + UpdateUserData['body'] + > + >; + uploadFile: HttpHandlerFactory< + | ToResponseUnion + | HttpResponseResolver< + StringifyPathParams>, + UploadFileData['body'] + > + >; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { baseUrl?: string }): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? 'https://petstore3.swagger.io/api/v3'; + const of: SingleHandlerFactories = { + addPet: (res, options) => + http.post( + toMswPath('/pet', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + createUser: (res, options) => + http.post( + toMswPath('/user', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + createUsersWithListInput: (res, options) => + http.post( + toMswPath('/user/createWithList', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + deleteOrder: (res, options) => + http.delete( + toMswPath('/store/order/{orderId}', baseUrl), + typeof res === 'object' && res.status + ? () => new HttpResponse(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + deletePet: (res, options) => + http.delete( + toMswPath('/pet/{petId}', baseUrl), + typeof res === 'object' && res.status + ? () => new HttpResponse(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + deleteUser: (res, options) => + http.delete( + toMswPath('/user/{username}', baseUrl), + typeof res === 'object' && res.status + ? () => new HttpResponse(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + findPetsByStatus: (res, options) => + http.get( + toMswPath('/pet/findByStatus', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + findPetsByTags: (res, options) => + http.get( + toMswPath('/pet/findByTags', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + getInventory: (res, options) => + http.get( + toMswPath('/store/inventory', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + getOrderById: (res, options) => + http.get( + toMswPath('/store/order/{orderId}', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + getPetById: (res, options) => + http.get( + toMswPath('/pet/{petId}', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + getUserByName: (res, options) => + http.get( + toMswPath('/user/{username}', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + loginUser: (res, options) => + http.get( + toMswPath('/user/login', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + logoutUser: (res, options) => + http.get( + toMswPath('/user/logout', baseUrl), + typeof res === 'object' && res.status + ? () => new HttpResponse(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + placeOrder: (res, options) => + http.post( + toMswPath('/store/order', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + updatePet: (res, options) => + http.put( + toMswPath('/pet', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + updatePetWithForm: (res, options) => + http.post( + toMswPath('/pet/{petId}', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + updateUser: (res, options) => + http.put( + toMswPath('/user/{username}', baseUrl), + typeof res === 'object' && res.status + ? () => new HttpResponse(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + uploadFile: (res, options) => + http.post( + toMswPath('/pet/{petId}/uploadImage', baseUrl), + typeof res === 'object' && res.status + ? () => HttpResponse.json(res.result ?? null, { status: res.status }) + : typeof res === 'function' + ? res + : resolveToNull, + options, + ), + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = ( + handler: (value: Value | (() => HttpResponse)) => HttpHandler, + override: Value | undefined, + ) => { + if (override != null) { + handlers.push(handler(override)); + } else { + if (onMissingMock === 'error') { + handlers.push( + handler( + () => + new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { + status: 501, + }), + ), + ); + } + } + }; + addRequiredHandler(of.addPet, overrides?.addPet); + addRequiredHandler(of.updatePet, overrides?.updatePet); + addRequiredHandler(of.findPetsByStatus, overrides?.findPetsByStatus); + addRequiredHandler(of.findPetsByTags, overrides?.findPetsByTags); + handlers.push(of.deletePet(overrides?.deletePet)); + addRequiredHandler(of.getPetById, overrides?.getPetById); + addRequiredHandler(of.updatePetWithForm, overrides?.updatePetWithForm); + addRequiredHandler(of.uploadFile, overrides?.uploadFile); + addRequiredHandler(of.getInventory, overrides?.getInventory); + addRequiredHandler(of.placeOrder, overrides?.placeOrder); + handlers.push(of.deleteOrder(overrides?.deleteOrder)); + addRequiredHandler(of.getOrderById, overrides?.getOrderById); + addRequiredHandler(of.createUser, overrides?.createUser); + addRequiredHandler(of.createUsersWithListInput, overrides?.createUsersWithListInput); + addRequiredHandler(of.loginUser, overrides?.loginUser); + handlers.push(of.logoutUser(overrides?.logoutUser)); + handlers.push(of.deleteUser(overrides?.deleteUser)); + addRequiredHandler(of.getUserByName, overrides?.getUserByName); + handlers.push(of.updateUser(overrides?.updateUser)); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/examples/openapi-ts-fetch/test/msw-types.test-d.ts b/examples/openapi-ts-fetch/test/msw-types.test-d.ts new file mode 100644 index 0000000000..9b298bf7aa --- /dev/null +++ b/examples/openapi-ts-fetch/test/msw-types.test-d.ts @@ -0,0 +1,138 @@ +import { type HttpHandler, HttpResponse } from 'msw'; +import { describe, expectTypeOf, it } from 'vitest'; + +import { createMswHandlerFactory } from '../src/client/msw.gen'; +import type { Order, Pet } from '../src/client/types.gen'; + +const createMock = createMswHandlerFactory(); + +describe('MSW plugin type-level tests', () => { + describe('static response values', () => { + it('accepts correct response type (Pet) with status', () => { + const pet: Pet = { name: 'Fido', photoUrls: [] }; + createMock.of.getPetById({ result: pet, status: 200 }); + createMock.of.addPet({ result: pet, status: 200 }); + createMock.of.updatePet({ result: pet, status: 200 }); + createMock.of.findPetsByStatus({ result: [pet], status: 200 }); + }); + + it('accepts correct response type (Order) with status', () => { + const order: Order = { id: 1, petId: 1, quantity: 1 }; + createMock.of.placeOrder({ result: order, status: 200 }); + createMock.of.getOrderById({ result: order, status: 200 }); + }); + + it('accepts correct response type (string) with status', () => { + createMock.of.loginUser({ result: 'session-token', status: 200 }); + }); + + it('accepts correct response type (record) with status', () => { + createMock.of.getInventory({ result: { available: 10, pending: 5 }, status: 200 }); + }); + + it('rejects wrong response type for getPetById', () => { + // @ts-expect-error - string is not a valid Pet response + createMock.of.getPetById({ result: 'wrong type', status: 200 }); + }); + + it('rejects wrong response type for addPet', () => { + // @ts-expect-error - number is not a valid Pet response + createMock.of.addPet({ result: 42, status: 200 }); + }); + + it('rejects wrong response type for findPetsByStatus', () => { + // @ts-expect-error - a single Pet is not Array + createMock.of.findPetsByStatus({ result: { name: 'Fido', photoUrls: [] }, status: 200 }); + }); + + it('rejects wrong response type for placeOrder', () => { + // @ts-expect-error - Pet is not a valid Order response + createMock.of.placeOrder({ result: { name: 'Fido', photoUrls: [] }, status: 200 }); + }); + + it('rejects wrong response type for loginUser', () => { + // @ts-expect-error - number is not a valid string response + createMock.of.loginUser({ result: 123, status: 200 }); + }); + + it('rejects wrong status code', () => { + const pet: Pet = { name: 'Fido', photoUrls: [] }; + // @ts-expect-error - 999 is not a valid status code + createMock.of.getPetById({ result: pet, status: 999 }); + }); + }); + + describe('void operations accept no arguments', () => { + it('logoutUser accepts no arguments', () => { + createMock.of.logoutUser(); + }); + + it('deletePet accepts no arguments', () => { + createMock.of.deletePet(); + }); + + it('deleteOrder accepts no arguments', () => { + createMock.of.deleteOrder(); + }); + + it('deleteUser accepts no arguments', () => { + createMock.of.deleteUser(); + }); + }); + + describe('resolver function typing', () => { + it('accepts HttpResponseResolver', () => { + createMock.of.getInventory(() => HttpResponse.json({ available: 1 })); + }); + + it('accepts async HttpResponseResolver', () => { + createMock.of.getInventory(async () => HttpResponse.json({ available: 1 })); + }); + + it('resolver for path-param operation receives typed params', () => { + createMock.of.getPetById(({ params }) => { + // params.petId should be string (StringifyPathParams) + expectTypeOf(params.petId).toEqualTypeOf>(); + return HttpResponse.json({ name: 'Test', photoUrls: [] }); + }); + }); + + it('resolver for body operation receives typed body via request', () => { + createMock.of.addPet(async ({ request }) => { + const body = await request.json(); + // body should be typed as Pet (AddPetData['body']) + expectTypeOf(body).toEqualTypeOf(); + return HttpResponse.json({ name: body.name, photoUrls: body.photoUrls }); + }); + }); + + it('resolver for void operation is typed correctly', () => { + createMock.of.logoutUser(() => HttpResponse.json(null)); + }); + + it('resolver for void operation with path params', () => { + createMock.of.deletePet(({ params }) => { + expectTypeOf(params.petId).toEqualTypeOf>(); + return new HttpResponse(null); + }); + }); + }); + + describe('return type', () => { + it('all handler creators return HttpHandler', () => { + const handler = createMock.of.getPetById({ + result: { name: 'Test', photoUrls: [] }, + status: 200, + }); + expectTypeOf(handler).toExtend(); + }); + }); + + describe('factory configuration', () => { + it('accepts optional config', () => { + createMswHandlerFactory(); + createMswHandlerFactory({}); + createMswHandlerFactory({ baseUrl: 'http://localhost:3000' }); + }); + }); +}); diff --git a/examples/openapi-ts-fetch/test/msw.test.ts b/examples/openapi-ts-fetch/test/msw.test.ts new file mode 100644 index 0000000000..4335ffbe7c --- /dev/null +++ b/examples/openapi-ts-fetch/test/msw.test.ts @@ -0,0 +1,264 @@ +import { HttpResponse } from 'msw'; +import { setupServer } from 'msw/node'; +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; + +import { client } from '../src/client/client.gen'; +import { createMswHandlerFactory } from '../src/client/msw.gen'; +import { + addPet, + findPetsByStatus, + getInventory, + getOrderById, + getPetById, + getUserByName, + uploadFile, +} from '../src/client/sdk.gen'; +import type { Pet } from '../src/client/types.gen'; + +const BASE_URL = 'http://localhost:3000/api/v3'; + +const createMock = createMswHandlerFactory({ baseUrl: BASE_URL }); + +const server = setupServer(); + +beforeAll(() => { + client.setConfig({ baseUrl: BASE_URL }); + server.listen({ onUnhandledRequest: 'error' }); +}); + +afterEach(() => { + server.resetHandlers(); +}); + +afterAll(() => { + server.close(); +}); + +describe('MSW plugin runtime tests', () => { + describe('static response value', () => { + it('returns static response for GET without path params', async () => { + const mockInventory = { available: 10, pending: 5 }; + server.use(createMock.of.getInventory({ result: mockInventory, status: 200 })); + + const result = await getInventory({ client }); + + expect(result.data).toEqual(mockInventory); + }); + + it('returns static response for GET with path params', async () => { + const mockPet: Pet = { + id: 1, + name: 'Fido', + photoUrls: ['https://example.com/fido.jpg'], + status: 'available', + }; + server.use(createMock.of.getPetById({ result: mockPet, status: 200 })); + + const result = await getPetById({ + client, + path: { petId: 1 }, + }); + + expect(result.data).toEqual(mockPet); + }); + + it('returns static response for GET with query params', async () => { + const mockPets: Pet[] = [ + { id: 1, name: 'Fido', photoUrls: [], status: 'available' }, + { id: 2, name: 'Rex', photoUrls: [], status: 'available' }, + ]; + server.use(createMock.of.findPetsByStatus({ result: mockPets, status: 200 })); + + const result = await findPetsByStatus({ + client, + query: { status: 'available' }, + }); + + expect(result.data).toEqual(mockPets); + }); + + it('returns static response for POST with body', async () => { + const mockPet: Pet = { + id: 10, + name: 'NewPet', + photoUrls: ['https://example.com/new.jpg'], + status: 'pending', + }; + server.use(createMock.of.addPet({ result: mockPet, status: 200 })); + + const result = await addPet({ + body: { + name: 'NewPet', + photoUrls: ['https://example.com/new.jpg'], + }, + client, + }); + + expect(result.data).toEqual(mockPet); + }); + }); + + describe('custom resolver function', () => { + it('supports custom resolver for GET', async () => { + server.use( + createMock.of.getPetById(({ params }) => + HttpResponse.json({ + id: Number(params.petId), + name: `Pet-${params.petId}`, + photoUrls: [], + status: 'available', + }), + ), + ); + + const result = await getPetById({ + client, + path: { petId: 42 }, + }); + + expect(result.data).toEqual({ + id: 42, + name: 'Pet-42', + photoUrls: [], + status: 'available', + }); + }); + + it('supports custom resolver with request body', async () => { + server.use( + createMock.of.addPet(async ({ request }) => { + const body = await request.json(); + return HttpResponse.json({ + id: 99, + ...body, + }); + }), + ); + + const result = await addPet({ + body: { + name: 'EchoedPet', + photoUrls: ['https://example.com/echo.jpg'], + }, + client, + }); + + expect(result.data).toMatchObject({ + id: 99, + name: 'EchoedPet', + }); + }); + + it('supports custom status codes', async () => { + server.use( + createMock.of.getPetById(() => + HttpResponse.json({ message: 'not found' }, { status: 404 }), + ), + ); + + const result = await getPetById({ + client, + path: { petId: 999 }, + }); + + expect(result.response.status).toBe(404); + expect(result.error).toEqual({ message: 'not found' }); + }); + }); + + describe('toMswPath conversion', () => { + it('handles numeric path param (e.g. /pet/{petId})', async () => { + const mockPet: Pet = { + id: 5, + name: 'PathTest', + photoUrls: [], + }; + server.use(createMock.of.getPetById({ result: mockPet, status: 200 })); + + const result = await getPetById({ + client, + path: { petId: 5 }, + }); + + expect(result.response.ok).toBe(true); + expect(result.data).toEqual(mockPet); + }); + + it('handles string path param (e.g. /user/{username})', async () => { + const mockUser = { + email: 'john@example.com', + firstName: 'John', + id: 1, + lastName: 'Doe', + username: 'john_doe', + }; + server.use(createMock.of.getUserByName({ result: mockUser, status: 200 })); + + const result = await getUserByName({ + client, + path: { username: 'john_doe' }, + }); + + expect(result.response.ok).toBe(true); + expect(result.data).toEqual(mockUser); + }); + + it('handles path param mid-path (e.g. /pet/{petId}/uploadImage)', async () => { + const mockResponse = { code: 200, message: 'uploaded', type: 'ok' }; + server.use(createMock.of.uploadFile({ result: mockResponse, status: 200 })); + + const result = await uploadFile({ + body: new Blob(['fake-image']), + client, + path: { petId: 7 }, + }); + + expect(result.response.ok).toBe(true); + expect(result.data).toEqual(mockResponse); + }); + + it('resolver receives correct path param values', async () => { + server.use( + createMock.of.getOrderById(({ params }) => { + // MSW normalizes path params to strings + expect(typeof params.orderId).toBe('string'); + return HttpResponse.json({ + complete: false, + id: Number(params.orderId), + petId: 1, + quantity: 1, + status: 'placed', + }); + }), + ); + + const result = await getOrderById({ + client, + path: { orderId: 123 }, + }); + + expect(result.data).toMatchObject({ id: 123 }); + }); + }); + + describe('handler override', () => { + it('later handlers override earlier ones', async () => { + server.use(createMock.of.getInventory({ result: { available: 1 }, status: 200 })); + server.use(createMock.of.getInventory({ result: { available: 999 }, status: 200 })); + + const result = await getInventory({ client }); + + expect(result.data).toEqual({ available: 999 }); + }); + }); + + describe('void operations', () => { + it('handles operations with no response body', async () => { + server.use(createMock.of.logoutUser()); + + const result = await (await import('../src/client/sdk.gen')).logoutUser({ client }); + + expect(result.response.ok).toBe(true); + }); + }); +}); diff --git a/examples/openapi-ts-fetch/vitest.config.ts b/examples/openapi-ts-fetch/vitest.config.ts new file mode 100644 index 0000000000..052a593edc --- /dev/null +++ b/examples/openapi-ts-fetch/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + typecheck: { + enabled: true, + }, + }, +}); diff --git a/packages/openapi-ts-tests/main/package.json b/packages/openapi-ts-tests/main/package.json index 42841f4be3..824f42d5bb 100644 --- a/packages/openapi-ts-tests/main/package.json +++ b/packages/openapi-ts-tests/main/package.json @@ -36,6 +36,7 @@ "eslint": "9.39.1", "fastify": "5.7.4", "ky": "1.14.3", + "msw": "2.10.2", "node-fetch": "3.3.2", "nuxt": "3.14.1592", "ofetch": "1.5.1", diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/index.ts new file mode 100644 index 0000000000..c42215da06 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ArrayWithArray, ArrayWithBooleans, ArrayWithNumbers, ArrayWithProperties, ArrayWithReferences, ArrayWithStrings, CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesErrors, CallWithDuplicateResponsesResponse, CallWithDuplicateResponsesResponses, CallWithNoContentResponseData, CallWithNoContentResponseResponses, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponse, CallWithResponseAndNoContentResponseResponses, CallWithResponseData, CallWithResponseResponse, CallWithResponseResponses, CallWithResponsesData, CallWithResponsesError, CallWithResponsesErrors, CallWithResponsesResponse, CallWithResponsesResponses, CallWithResultFromHeaderData, CallWithResultFromHeaderErrors, CallWithResultFromHeaderResponses, CallWithWeirdParameterNamesData, ClientOptions, CollectionFormatData, CommentWithBackticks, CommentWithBackticksAndQuotes, CommentWithBreaks, CommentWithExpressionPlaceholders, CommentWithQuotes, CommentWithReservedCharacters, CommentWithSlashes, ComplexTypesData, ComplexTypesErrors, ComplexTypesResponse, ComplexTypesResponses, Date, Default, DeleteCallWithoutParametersAndResponseData, DictionaryWithArray, DictionaryWithDictionary, DictionaryWithProperties, DictionaryWithReference, DictionaryWithString, DummyAData, DummyAResponses, DummyBData, DummyBResponses, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, EnumFromDescription, EnumWithExtensions, EnumWithNumbers, EnumWithStrings, ExternalRefA, ExternalRefB, ExternalSharedModel, FailureFailure, FooWowData, FooWowResponses, GetCallWithoutParametersAndResponseData, HeadCallWithoutParametersAndResponseData, ModelThatExtends, ModelThatExtendsExtends, ModelWithArray, ModelWithBoolean, ModelWithCircularReference, ModelWithDictionary, ModelWithDuplicateImports, ModelWithDuplicateProperties, ModelWithEnum, ModelWithEnumFromDescription, ModelWithInteger, ModelWithNestedEnums, ModelWithNestedProperties, ModelWithNullableString, ModelWithOrderedProperties, ModelWithPattern, ModelWithPatternWritable, ModelWithProperties, ModelWithPropertiesWritable, ModelWithReference, ModelWithReferenceWritable, ModelWithString, ModelWithStringError, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, NonAsciiæøåÆøÅöôêÊ字符串Responses, NonAsciiStringæøåÆøÅöôêÊ字符串, OptionsCallWithoutParametersAndResponseData, ParameterActivityParams, PatchApiVbyApiVersionNoTagData, PatchApiVbyApiVersionNoTagResponses, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionBodyData, PostApiVbyApiVersionBodyError, PostApiVbyApiVersionBodyErrors, PostApiVbyApiVersionBodyResponse, PostApiVbyApiVersionBodyResponses, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, ResponsePostActivityResponse, ServiceWithEmptyTagData, SimpleBoolean, SimpleFile, SimpleInteger, SimpleReference, SimpleString, SimpleStringWithPattern, TestErrorCodeData, TestErrorCodeErrors, TestErrorCodeResponses, TypesData, TypesResponse, TypesResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/msw.gen.ts new file mode 100644 index 0000000000..c56eab0717 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/msw.gen.ts @@ -0,0 +1,166 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { CallWithDuplicateResponsesResponses, CallWithNoContentResponseResponses, CallWithParametersData, CallWithResponseAndNoContentResponseResponses, CallWithResponsesResponses, CallWithResultFromHeaderResponses, CallWithWeirdParameterNamesData, ComplexTypesResponses, DummyAResponses, DummyBResponses, NonAsciiæøåÆøÅöôêÊ字符串Responses, PostApiVbyApiVersionBodyData, PostApiVbyApiVersionBodyResponses, TestErrorCodeResponses, TypesData, TypesResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type OptionalHttpHandlerFactory = (responseOrResolver?: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type StringifyPathParams = { + [K in keyof T]-?: string | ReadonlyArray; +}; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + serviceWithEmptyTag: OptionalHttpHandlerFactory; + patchApiVbyApiVersionNoTag: OptionalHttpHandlerFactory; + fooWow: OptionalHttpHandlerFactory; + deleteCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + getCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + headCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + optionsCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + patchCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + postCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + putCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + callWithDescriptions: OptionalHttpHandlerFactory; + callWithParameters: OptionalHttpHandlerFactory>, never>>; + callWithWeirdParameterNames: OptionalHttpHandlerFactory>, CallWithWeirdParameterNamesData['body']>>; + callWithDefaultParameters: OptionalHttpHandlerFactory; + callWithDefaultOptionalParameters: OptionalHttpHandlerFactory; + callToTestOrderOfParams: OptionalHttpHandlerFactory; + duplicateName: OptionalHttpHandlerFactory; + duplicateName2: OptionalHttpHandlerFactory; + duplicateName3: OptionalHttpHandlerFactory; + duplicateName4: OptionalHttpHandlerFactory; + callWithNoContentResponse: OptionalHttpHandlerFactory | HttpResponseResolver>; + callWithResponseAndNoContentResponse: HttpHandlerFactory | HttpResponseResolver>; + dummyA: OptionalHttpHandlerFactory | HttpResponseResolver>; + dummyB: OptionalHttpHandlerFactory | HttpResponseResolver>; + callWithResponse: OptionalHttpHandlerFactory; + callWithDuplicateResponses: HttpHandlerFactory | HttpResponseResolver>; + callWithResponses: HttpHandlerFactory | HttpResponseResolver>; + collectionFormat: OptionalHttpHandlerFactory; + types: HttpHandlerFactory | HttpResponseResolver>, never>>; + complexTypes: HttpHandlerFactory | HttpResponseResolver>; + callWithResultFromHeader: OptionalHttpHandlerFactory | HttpResponseResolver>; + testErrorCode: OptionalHttpHandlerFactory | HttpResponseResolver>; + nonAsciiæøåÆøÅöôêÊ字符串: HttpHandlerFactory | HttpResponseResolver>; + postApiVbyApiVersionBody: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? 'http://localhost:3000/base'; + const of: SingleHandlerFactories = { + serviceWithEmptyTag: (res, options) => http.get(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + patchApiVbyApiVersionNoTag: (res, options) => http.patch(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + fooWow: (res, options) => http.put(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + deleteCallWithoutParametersAndResponse: (res, options) => http.delete(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + getCallWithoutParametersAndResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + headCallWithoutParametersAndResponse: (res, options) => http.head(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + optionsCallWithoutParametersAndResponse: (res, options) => http.options(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + patchCallWithoutParametersAndResponse: (res, options) => http.patch(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + postCallWithoutParametersAndResponse: (res, options) => http.post(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + putCallWithoutParametersAndResponse: (res, options) => http.put(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDescriptions: (res, options) => http.post(toMswPath('/api/v{api-version}/descriptions/', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithParameters: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters/{parameterPath}', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithWeirdParameterNames: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDefaultParameters: (res, options) => http.get(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDefaultOptionalParameters: (res, options) => http.post(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callToTestOrderOfParams: (res, options) => http.put(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName: (res, options) => http.delete(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName2: (res, options) => http.get(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName3: (res, options) => http.post(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName4: (res, options) => http.put(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithNoContentResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/no-content', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponseAndNoContentResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/response-and-no-content', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + dummyA: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/a', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + dummyB: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/b', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDuplicateResponses: (res, options) => http.post(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponses: (res, options) => http.put(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + collectionFormat: (res, options) => http.get(toMswPath('/api/v{api-version}/collectionFormat', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + types: (res, options) => http.get(toMswPath('/api/v{api-version}/types', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + complexTypes: (res, options) => http.get(toMswPath('/api/v{api-version}/complex', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResultFromHeader: (res, options) => http.post(toMswPath('/api/v{api-version}/header', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + testErrorCode: (res, options) => http.post(toMswPath('/api/v{api-version}/error', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + nonAsciiæøåÆøÅöôêÊ字符串: (res, options) => http.post(toMswPath('/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postApiVbyApiVersionBody: (res, options) => http.post(toMswPath('/api/v{api-version}/body', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + handlers.push(of.serviceWithEmptyTag(overrides?.serviceWithEmptyTag)); + handlers.push(of.patchApiVbyApiVersionNoTag(overrides?.patchApiVbyApiVersionNoTag)); + handlers.push(of.fooWow(overrides?.fooWow)); + handlers.push(of.deleteCallWithoutParametersAndResponse(overrides?.deleteCallWithoutParametersAndResponse)); + handlers.push(of.getCallWithoutParametersAndResponse(overrides?.getCallWithoutParametersAndResponse)); + handlers.push(of.headCallWithoutParametersAndResponse(overrides?.headCallWithoutParametersAndResponse)); + handlers.push(of.optionsCallWithoutParametersAndResponse(overrides?.optionsCallWithoutParametersAndResponse)); + handlers.push(of.patchCallWithoutParametersAndResponse(overrides?.patchCallWithoutParametersAndResponse)); + handlers.push(of.postCallWithoutParametersAndResponse(overrides?.postCallWithoutParametersAndResponse)); + handlers.push(of.putCallWithoutParametersAndResponse(overrides?.putCallWithoutParametersAndResponse)); + handlers.push(of.callWithDescriptions(overrides?.callWithDescriptions)); + handlers.push(of.callWithParameters(overrides?.callWithParameters)); + handlers.push(of.callWithWeirdParameterNames(overrides?.callWithWeirdParameterNames)); + handlers.push(of.callWithDefaultParameters(overrides?.callWithDefaultParameters)); + handlers.push(of.callWithDefaultOptionalParameters(overrides?.callWithDefaultOptionalParameters)); + handlers.push(of.callToTestOrderOfParams(overrides?.callToTestOrderOfParams)); + handlers.push(of.duplicateName(overrides?.duplicateName)); + handlers.push(of.duplicateName2(overrides?.duplicateName2)); + handlers.push(of.duplicateName3(overrides?.duplicateName3)); + handlers.push(of.duplicateName4(overrides?.duplicateName4)); + handlers.push(of.callWithNoContentResponse(overrides?.callWithNoContentResponse)); + addRequiredHandler(of.callWithResponseAndNoContentResponse, overrides?.callWithResponseAndNoContentResponse); + handlers.push(of.dummyA(overrides?.dummyA)); + handlers.push(of.dummyB(overrides?.dummyB)); + handlers.push(of.callWithResponse(overrides?.callWithResponse)); + addRequiredHandler(of.callWithDuplicateResponses, overrides?.callWithDuplicateResponses); + addRequiredHandler(of.callWithResponses, overrides?.callWithResponses); + handlers.push(of.collectionFormat(overrides?.collectionFormat)); + addRequiredHandler(of.types, overrides?.types); + addRequiredHandler(of.complexTypes, overrides?.complexTypes); + handlers.push(of.callWithResultFromHeader(overrides?.callWithResultFromHeader)); + handlers.push(of.testErrorCode(overrides?.testErrorCode)); + addRequiredHandler(of.nonAsciiæøåÆøÅöôêÊ字符串, overrides?.nonAsciiæøåÆøÅöôêÊ字符串); + addRequiredHandler(of.postApiVbyApiVersionBody, overrides?.postApiVbyApiVersionBody); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/types.gen.ts new file mode 100644 index 0000000000..d69b074040 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/default/types.gen.ts @@ -0,0 +1,1190 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: 'http://localhost:3000/base' | (string & {}); +}; + +export type ExternalRefA = ExternalSharedModel; + +export type ExternalRefB = ExternalSharedModel; + +/** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ +export type CommentWithBreaks = number; + +/** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ +export type CommentWithBackticks = number; + +/** + * Testing backticks and quotes in string: `backticks`, 'quotes', "double quotes" and ```multiple backticks``` should work + */ +export type CommentWithBackticksAndQuotes = number; + +/** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ +export type CommentWithSlashes = number; + +/** + * Testing expression placeholders in string: ${expression} should work + */ +export type CommentWithExpressionPlaceholders = number; + +/** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ +export type CommentWithQuotes = number; + +/** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ +export type CommentWithReservedCharacters = number; + +/** + * This is a simple number + */ +export type SimpleInteger = number; + +/** + * This is a simple boolean + */ +export type SimpleBoolean = boolean; + +/** + * This is a simple string + */ +export type SimpleString = string; + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export type NonAsciiStringæøåÆøÅöôêÊ字符串 = string; + +/** + * This is a simple file + */ +export type SimpleFile = Blob | File; + +export type SimpleReference = ModelWithString; + +/** + * This is a simple string + */ +export type SimpleStringWithPattern = string; + +/** + * This is a simple enum with strings + */ +export type EnumWithStrings = 'Success' | 'Warning' | 'Error' | '\'Single Quote\'' | '"Double Quotes"' | 'Non-ascii: øæåôöØÆÅÔÖ字符串'; + +/** + * This is a simple enum with numbers + */ +export type EnumWithNumbers = 1 | 2 | 3 | 1.1 | 1.2 | 1.3 | 100 | 200 | 300 | -100 | -200 | -300 | -1.1 | -1.2 | -1.3; + +/** + * Success=1,Warning=2,Error=3 + */ +export type EnumFromDescription = number; + +/** + * This is a simple enum with numbers + */ +export type EnumWithExtensions = 200 | 400 | 500; + +/** + * This is a simple array with numbers + */ +export type ArrayWithNumbers = Array; + +/** + * This is a simple array with booleans + */ +export type ArrayWithBooleans = Array; + +/** + * This is a simple array with strings + */ +export type ArrayWithStrings = Array; + +/** + * This is a simple array with references + */ +export type ArrayWithReferences = Array; + +/** + * This is a simple array containing an array + */ +export type ArrayWithArray = Array>; + +/** + * This is a simple array with properties + */ +export type ArrayWithProperties = Array<{ + foo?: string; + bar?: string; +}>; + +/** + * This is a string dictionary + */ +export type DictionaryWithString = { + [key: string]: string; +}; + +/** + * This is a string reference + */ +export type DictionaryWithReference = { + [key: string]: ModelWithString; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithArray = { + [key: string]: Array; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithDictionary = { + [key: string]: { + [key: string]: string; + }; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithProperties = { + [key: string]: { + foo?: string; + bar?: string; + }; +}; + +/** + * This is a type-only model that defines Date as a string + */ +export type Date = string; + +/** + * This is a model with one number property + */ +export type ModelWithInteger = { + /** + * This is a simple number property + */ + prop?: number; +}; + +/** + * This is a model with one boolean property + */ +export type ModelWithBoolean = { + /** + * This is a simple boolean property + */ + prop?: boolean; +}; + +/** + * This is a model with one string property + */ +export type ModelWithString = { + /** + * This is a simple string property + */ + prop?: string; +}; + +/** + * This is a model with one string property + */ +export type ModelWithStringError = { + /** + * This is a simple string property + */ + prop?: string; +}; + +/** + * This is a model with one string property + */ +export type ModelWithNullableString = { + /** + * This is a simple string property + */ + nullableProp?: string | null; + /** + * This is a simple string property + */ + nullableRequiredProp: string | null; +}; + +/** + * This is a model with one enum + */ +export type ModelWithEnum = { + /** + * This is a simple enum with strings + */ + test?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; + /** + * These are the HTTP error code enums + */ + statusCode?: '100' | '200 FOO' | '300 FOO_BAR' | '400 foo-bar' | '500 foo.bar' | '600 foo&bar'; + /** + * Simple boolean enum + */ + bool?: true; +}; + +/** + * This is a model with one enum + */ +export type ModelWithEnumFromDescription = { + /** + * Success=1,Warning=2,Error=3 + */ + test?: number; +}; + +/** + * This is a model with nested enums + */ +export type ModelWithNestedEnums = { + dictionaryWithEnum?: { + [key: string]: 'Success' | 'Warning' | 'Error'; + }; + dictionaryWithEnumFromDescription?: { + [key: string]: number; + }; + arrayWithEnum?: Array<'Success' | 'Warning' | 'Error'>; + arrayWithDescription?: Array; +}; + +/** + * This is a model with one property containing a reference + */ +export type ModelWithReference = { + prop?: ModelWithProperties; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArray = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing a dictionary + */ +export type ModelWithDictionary = { + prop?: { + [key: string]: string; + }; +}; + +/** + * This is a model with one property containing a circular reference + */ +export type ModelWithCircularReference = { + prop?: ModelWithCircularReference; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithProperties = { + required: string; + readonly requiredAndReadOnly: string; + string?: string; + number?: number; + boolean?: boolean; + reference?: ModelWithString; + 'property with space'?: string; + default?: string; + try?: string; + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithNestedProperties = { + readonly first: { + readonly second: { + readonly third: string; + }; + }; +}; + +/** + * This is a model with duplicated properties + */ +export type ModelWithDuplicateProperties = { + prop?: ModelWithString; +}; + +/** + * This is a model with ordered properties + */ +export type ModelWithOrderedProperties = { + zebra?: string; + apple?: string; + hawaii?: string; +}; + +/** + * This is a model with duplicated imports + */ +export type ModelWithDuplicateImports = { + propA?: ModelWithString; + propB?: ModelWithString; + propC?: ModelWithString; +}; + +/** + * This is a model that extends another model + */ +export type ModelThatExtends = ModelWithString & { + propExtendsA?: string; + propExtendsB?: ModelWithString; +}; + +/** + * This is a model that extends another model + */ +export type ModelThatExtendsExtends = ModelWithString & ModelThatExtends & { + propExtendsC?: string; + propExtendsD?: ModelWithString; +}; + +export type Default = { + name?: string; +}; + +/** + * This is a model that contains a some patterns + */ +export type ModelWithPattern = { + key: string; + name: string; + readonly enabled?: boolean; + readonly modified?: string; + id?: string; + text?: string; + patternWithSingleQuotes?: string; + patternWithNewline?: string; + patternWithBacktick?: string; + patternWithUnicode?: string; +}; + +export type ParameterActivityParams = { + description?: string; + graduate_id?: number; + organization_id?: number; + parent_activity?: number; + post_id?: number; +}; + +export type ResponsePostActivityResponse = { + description?: string; + graduate_id?: number; + organization_id?: number; + parent_activity_id?: number; + post_id?: number; +}; + +export type FailureFailure = { + error?: string; + message?: string; + reference_code?: string; +}; + +export type ExternalSharedModel = { + id: string; + name?: string; +}; + +/** + * This is a model with one property containing a reference + */ +export type ModelWithReferenceWritable = { + prop?: ModelWithPropertiesWritable; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithPropertiesWritable = { + required: string; + string?: string; + number?: number; + boolean?: boolean; + reference?: ModelWithString; + 'property with space'?: string; + default?: string; + try?: string; +}; + +/** + * This is a model that contains a some patterns + */ +export type ModelWithPatternWritable = { + key: string; + name: string; + id?: string; + text?: string; + patternWithSingleQuotes?: string; + patternWithNewline?: string; + patternWithBacktick?: string; + patternWithUnicode?: string; +}; + +export type ServiceWithEmptyTagData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type PatchApiVbyApiVersionNoTagData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type PatchApiVbyApiVersionNoTagResponses = { + /** + * OK + */ + default: unknown; +}; + +export type FooWowData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type FooWowResponses = { + /** + * OK + */ + default: unknown; +}; + +export type DeleteCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type GetCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type HeadCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type OptionsCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PatchCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PostCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PutCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type CallWithDescriptionsData = { + body?: never; + path?: never; + query?: { + /** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ + parameterWithBreaks?: string; + /** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ + parameterWithBackticks?: string; + /** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ + parameterWithSlashes?: string; + /** + * Testing expression placeholders in string: ${expression} should work + */ + parameterWithExpressionPlaceholders?: string; + /** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ + parameterWithQuotes?: string; + /** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ + parameterWithReservedCharacters?: string; + }; + url: '/api/v{api-version}/descriptions/'; +}; + +export type CallWithParametersData = { + body?: never; + headers: { + /** + * This is the parameter that goes into the header + */ + parameterHeader: string; + }; + path: { + /** + * This is the parameter that goes into the path + */ + parameterPath: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query: { + /** + * This is the parameter that goes into the query params + */ + parameterQuery: string; + }; + url: '/api/v{api-version}/parameters/{parameterPath}'; +}; + +export type CallWithWeirdParameterNamesData = { + /** + * This is the parameter that is sent as request body + */ + body: string; + headers: { + /** + * This is the parameter that goes into the request header + */ + 'parameter.header': string; + }; + path: { + /** + * This is the parameter that goes into the path + */ + 'parameter.path.1'?: string; + /** + * This is the parameter that goes into the path + */ + 'parameter-path-2'?: string; + /** + * This is the parameter that goes into the path + */ + 'PARAMETER-PATH-3'?: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query: { + /** + * This is the parameter with a reserved keyword + */ + default?: string; + /** + * This is the parameter that goes into the request query params + */ + 'parameter-query': string; + }; + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}'; +}; + +export type CallWithDefaultParametersData = { + body?: never; + path?: never; + query: { + /** + * This is a simple string with default value + */ + parameterString: string; + /** + * This is a simple number with default value + */ + parameterNumber: number; + /** + * This is a simple boolean with default value + */ + parameterBoolean: boolean; + /** + * This is a simple enum with default value + */ + parameterEnum: 'Success' | 'Warning' | 'Error'; + /** + * This is a model with one string property + */ + parameterModel: { + /** + * This is a simple string property + */ + prop?: string; + }; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallWithDefaultOptionalParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string that is optional with default value + */ + parameterString?: string; + /** + * This is a simple number that is optional with default value + */ + parameterNumber?: number; + /** + * This is a simple boolean that is optional with default value + */ + parameterBoolean?: boolean; + /** + * This is a simple enum that is optional with default value + */ + parameterEnum?: 'Success' | 'Warning' | 'Error'; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallToTestOrderOfParamsData = { + body?: never; + path?: never; + query: { + /** + * This is a optional string with default + */ + parameterOptionalStringWithDefault?: string; + /** + * This is a optional string with empty default + */ + parameterOptionalStringWithEmptyDefault?: string; + /** + * This is a optional string with no default + */ + parameterOptionalStringWithNoDefault?: string; + /** + * This is a string with default + */ + parameterStringWithDefault: string; + /** + * This is a string with empty default + */ + parameterStringWithEmptyDefault: string; + /** + * This is a string with no default + */ + parameterStringWithNoDefault: string; + /** + * This is a string that can be null with no default + */ + parameterStringNullableWithNoDefault?: string | null; + /** + * This is a string that can be null with default + */ + parameterStringNullableWithDefault?: string | null; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type DuplicateNameData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName2Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName3Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName4Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type CallWithNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-content'; +}; + +export type CallWithNoContentResponseResponses = { + /** + * Success + */ + 204: unknown; +}; + +export type CallWithResponseAndNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/response-and-no-content'; +}; + +export type CallWithResponseAndNoContentResponseResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: unknown; +}; + +export type CallWithResponseAndNoContentResponseResponse = CallWithResponseAndNoContentResponseResponses[keyof CallWithResponseAndNoContentResponseResponses]; + +export type DummyAData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/a'; +}; + +export type DummyAResponses = { + /** + * Success + */ + 204: unknown; +}; + +export type DummyBData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/b'; +}; + +export type DummyBResponses = { + /** + * Success + */ + 204: unknown; +}; + +export type CallWithResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponseResponses = { + /** + * Message for default response + */ + default: ModelWithString; +}; + +export type CallWithResponseResponse = CallWithResponseResponses[keyof CallWithResponseResponses]; + +export type CallWithDuplicateResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithDuplicateResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for default response + */ + default: ModelWithString; +}; + +export type CallWithDuplicateResponsesError = CallWithDuplicateResponsesErrors[keyof CallWithDuplicateResponsesErrors]; + +export type CallWithDuplicateResponsesResponses = { + /** + * Message for 201 response + */ + 201: ModelWithString; + /** + * Message for 202 response + */ + 202: ModelWithString; +}; + +export type CallWithDuplicateResponsesResponse = CallWithDuplicateResponsesResponses[keyof CallWithDuplicateResponsesResponses]; + +export type CallWithResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for default response + */ + default: ModelWithString; +}; + +export type CallWithResponsesError = CallWithResponsesErrors[keyof CallWithResponsesErrors]; + +export type CallWithResponsesResponses = { + /** + * Message for 200 response + */ + 200: { + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; + readonly value?: Array; + }; + /** + * Message for 201 response + */ + 201: ModelThatExtends; + /** + * Message for 202 response + */ + 202: ModelThatExtendsExtends; +}; + +export type CallWithResponsesResponse = CallWithResponsesResponses[keyof CallWithResponsesResponses]; + +export type CollectionFormatData = { + body?: never; + path?: never; + query: { + /** + * This is an array parameter that is sent as csv format (comma-separated values) + */ + parameterArrayCSV: Array; + /** + * This is an array parameter that is sent as ssv format (space-separated values) + */ + parameterArraySSV: Array; + /** + * This is an array parameter that is sent as tsv format (tab-separated values) + */ + parameterArrayTSV: Array; + /** + * This is an array parameter that is sent as pipes format (pipe-separated values) + */ + parameterArrayPipes: Array; + /** + * This is an array parameter that is sent as multi format (multiple parameter instances) + */ + parameterArrayMulti: Array; + }; + url: '/api/v{api-version}/collectionFormat'; +}; + +export type TypesData = { + body?: never; + path?: { + /** + * This is a number parameter + */ + id?: number; + }; + query: { + /** + * This is a number parameter + */ + parameterNumber: number; + /** + * This is a string parameter + */ + parameterString: string; + /** + * This is a boolean parameter + */ + parameterBoolean: boolean; + /** + * This is an array parameter + */ + parameterArray: Array; + /** + * This is a dictionary parameter + */ + parameterDictionary: { + [key: string]: unknown; + }; + /** + * This is an enum parameter + */ + parameterEnum: 'Success' | 'Warning' | 'Error'; + }; + url: '/api/v{api-version}/types'; +}; + +export type TypesResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Response is a simple string + */ + 201: string; + /** + * Response is a simple boolean + */ + 202: boolean; + /** + * Response is a simple object + */ + 203: { + [key: string]: unknown; + }; +}; + +export type TypesResponse = TypesResponses[keyof TypesResponses]; + +export type ComplexTypesData = { + body?: never; + path?: never; + query: { + /** + * Parameter containing object + */ + parameterObject: { + first?: { + second?: { + third?: string; + }; + }; + }; + /** + * This is a model with one string property + */ + parameterReference: { + /** + * This is a simple string property + */ + prop?: string; + }; + }; + url: '/api/v{api-version}/complex'; +}; + +export type ComplexTypesErrors = { + /** + * 400 server error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type ComplexTypesResponses = { + /** + * Successful response + */ + 200: Array; +}; + +export type ComplexTypesResponse = ComplexTypesResponses[keyof ComplexTypesResponses]; + +export type CallWithResultFromHeaderData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/header'; +}; + +export type CallWithResultFromHeaderErrors = { + /** + * 400 server error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type CallWithResultFromHeaderResponses = { + /** + * Successful response + */ + 200: unknown; +}; + +export type TestErrorCodeData = { + body?: never; + path?: never; + query: { + /** + * Status code to return + */ + status: string; + }; + url: '/api/v{api-version}/error'; +}; + +export type TestErrorCodeErrors = { + /** + * Custom message: Internal Server Error + */ + 500: unknown; + /** + * Custom message: Not Implemented + */ + 501: unknown; + /** + * Custom message: Bad Gateway + */ + 502: unknown; + /** + * Custom message: Service Unavailable + */ + 503: unknown; +}; + +export type TestErrorCodeResponses = { + /** + * Custom message: Successful response + */ + 200: unknown; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Data = { + body?: never; + path?: never; + query: { + /** + * Dummy input param + */ + nonAsciiParamæøåÆØÅöôêÊ: number; + }; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Responses = { + /** + * Successful response + */ + 200: NonAsciiStringæøåÆøÅöôêÊ字符串; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Response = NonAsciiæøåÆøÅöôêÊ字符串Responses[keyof NonAsciiæøåÆøÅöôêÊ字符串Responses]; + +export type PostApiVbyApiVersionBodyData = { + /** + * Body should not be unknown + */ + body: ParameterActivityParams; + path?: never; + query?: never; + url: '/api/v{api-version}/body'; +}; + +export type PostApiVbyApiVersionBodyErrors = { + /** + * Bad Request + */ + 400: FailureFailure; + /** + * Internal Server Error + */ + 500: FailureFailure; +}; + +export type PostApiVbyApiVersionBodyError = PostApiVbyApiVersionBodyErrors[keyof PostApiVbyApiVersionBodyErrors]; + +export type PostApiVbyApiVersionBodyResponses = { + /** + * OK + */ + 200: ResponsePostActivityResponse; +}; + +export type PostApiVbyApiVersionBodyResponse = PostApiVbyApiVersionBodyResponses[keyof PostApiVbyApiVersionBodyResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/index.ts new file mode 100644 index 0000000000..b545604fba --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses, Person, PostFooData, PostFooResponse, PostFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/msw.gen.ts new file mode 100644 index 0000000000..096bd2386d --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/msw.gen.ts @@ -0,0 +1,64 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses, PostFooData, PostFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: HttpHandlerFactory | HttpResponseResolver>; + postFoo: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? ''; + const of: SingleHandlerFactories = { + getFoo: (res, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postFoo: (res, options) => http.post(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + addRequiredHandler(of.getFoo, overrides?.getFoo); + addRequiredHandler(of.postFoo, overrides?.postFoo); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/types.gen.ts new file mode 100644 index 0000000000..bf9849161c --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example-disabled/types.gen.ts @@ -0,0 +1,50 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: string; +}; + +export type Person = { + firstName?: string; + lastName?: string; + age?: number; +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: Person; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; + +export type PostFooData = { + body: string; + path?: never; + query?: never; + url: '/foo'; +}; + +export type PostFooResponses = { + /** + * OK + */ + 200: { + fullName?: string; + age?: number; + }; + /** + * SUCCESSFUL + */ + 204: unknown; +}; + +export type PostFooResponse = PostFooResponses[keyof PostFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/index.ts new file mode 100644 index 0000000000..b545604fba --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses, Person, PostFooData, PostFooResponse, PostFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/msw.gen.ts new file mode 100644 index 0000000000..e7eea1763e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/msw.gen.ts @@ -0,0 +1,57 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses, PostFooData, PostFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type OptionalHttpHandlerFactory = (responseOrResolver?: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: OptionalHttpHandlerFactory | HttpResponseResolver>; + postFoo: OptionalHttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? ''; + const of: SingleHandlerFactories = { + getFoo: (res = { result: { + firstName: 'Marry', + lastName: 'Jane', + age: 30 + }, status: 200 }, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postFoo: (res = { result: { fullName: 'John Doe', age: 34 }, status: 200 }, options) => http.post(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const overrides = options?.overrides; + const handlers: Array = []; + handlers.push(of.getFoo(overrides?.getFoo)); + handlers.push(of.postFoo(overrides?.postFoo)); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/types.gen.ts new file mode 100644 index 0000000000..bf9849161c --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/types.gen.ts @@ -0,0 +1,50 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: string; +}; + +export type Person = { + firstName?: string; + lastName?: string; + age?: number; +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: Person; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; + +export type PostFooData = { + body: string; + path?: never; + query?: never; + url: '/foo'; +}; + +export type PostFooResponses = { + /** + * OK + */ + 200: { + fullName?: string; + age?: number; + }; + /** + * SUCCESSFUL + */ + 204: unknown; +}; + +export type PostFooResponse = PostFooResponses[keyof PostFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/index.ts new file mode 100644 index 0000000000..f389f85547 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/msw.gen.ts new file mode 100644 index 0000000000..59e8e0c5fb --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/msw.gen.ts @@ -0,0 +1,61 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? 'https://foo.com/v1'; + const of: SingleHandlerFactories = { + getFoo: (res, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + addRequiredHandler(of.getFoo, overrides?.getFoo); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/types.gen.ts new file mode 100644 index 0000000000..b54728320a --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/servers/types.gen.ts @@ -0,0 +1,21 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: 'https://foo.com/v1' | (string & {}); +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: string; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/index.ts new file mode 100644 index 0000000000..2ef59c1f0c --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { _3eNum1Период, _400, AdditionalPropertiesIntegerIssue, AdditionalPropertiesUnknownIssue, AdditionalPropertiesUnknownIssue2, AdditionalPropertiesUnknownIssue3, AdditionalPropertiesUnknownIssueWritable, AnyOfAnyAndNull, AnyOfArrays, ApiVVersionODataControllerCountData, ApiVVersionODataControllerCountResponse, ApiVVersionODataControllerCountResponses, ArrayWithAnyOfProperties, ArrayWithArray, ArrayWithBooleans, ArrayWithNumbers, ArrayWithProperties, ArrayWithReferences, ArrayWithStrings, CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesErrors, CallWithDuplicateResponsesResponse, CallWithDuplicateResponsesResponses, CallWithNoContentResponseData, CallWithNoContentResponseResponse, CallWithNoContentResponseResponses, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponse, CallWithResponseAndNoContentResponseResponses, CallWithResponseData, CallWithResponseResponse, CallWithResponseResponses, CallWithResponsesData, CallWithResponsesError, CallWithResponsesErrors, CallWithResponsesResponse, CallWithResponsesResponses, CallWithResultFromHeaderData, CallWithResultFromHeaderErrors, CallWithResultFromHeaderResponses, CallWithWeirdParameterNamesData, CamelCaseCommentWithBreaks, CharactersInDescription, ClientOptions, CollectionFormatData, CommentWithBackticks, CommentWithBackticksAndQuotes, CommentWithBreaks, CommentWithExpressionPlaceholders, CommentWithQuotes, CommentWithReservedCharacters, CommentWithSlashes, ComplexParamsData, ComplexParamsResponse, ComplexParamsResponses, ComplexTypesData, ComplexTypesErrors, ComplexTypesResponse, ComplexTypesResponses, CompositionBaseModel, CompositionExtendedModel, CompositionWithAllOfAndNullable, CompositionWithAnyOf, CompositionWithAnyOfAndNullable, CompositionWithAnyOfAnonymous, CompositionWithNestedAnyAndTypeNull, CompositionWithNestedAnyOfAndNull, CompositionWithOneOf, CompositionWithOneOfAndComplexArrayDictionary, CompositionWithOneOfAndNullable, CompositionWithOneOfAndProperties, CompositionWithOneOfAndSimpleArrayDictionary, CompositionWithOneOfAndSimpleDictionary, CompositionWithOneOfAnonymous, CompositionWithOneOfDiscriminator, ConstValue, Default, DeleteCallWithoutParametersAndResponseData, DeleteFooData, DeleteFooData2, DeleteFooData3, DeprecatedCallData, DeprecatedModel, DictionaryWithArray, DictionaryWithDictionary, DictionaryWithProperties, DictionaryWithPropertiesAndAdditionalProperties, DictionaryWithReference, DictionaryWithString, DummyAData, DummyAResponse, DummyAResponses, DummyBData, DummyBResponse, DummyBResponses, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, EnumFromDescription, EnumWithExtensions, EnumWithNumbers, EnumWithReplacedCharacters, EnumWithStrings, EnumWithXEnumNames, ExportData, ExternalRefA, ExternalRefB, ExternalSharedModel, File, FileResponseData, FileResponseResponse, FileResponseResponses, FileWritable, FooWowData, FooWowResponses, FreeFormObjectWithAdditionalPropertiesEqEmptyObject, FreeFormObjectWithAdditionalPropertiesEqTrue, FreeFormObjectWithoutAdditionalProperties, GenericSchemaDuplicateIssue1SystemBoolean, GenericSchemaDuplicateIssue1SystemBooleanWritable, GenericSchemaDuplicateIssue1SystemString, GenericSchemaDuplicateIssue1SystemStringWritable, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationError, GetApiVbyApiVersionSimpleOperationErrors, GetApiVbyApiVersionSimpleOperationResponse, GetApiVbyApiVersionSimpleOperationResponses, GetCallWithOptionalParamData, GetCallWithoutParametersAndResponseData, HeadCallWithoutParametersAndResponseData, Import, ImportData, ImportResponse, ImportResponses, IoK8sApimachineryPkgApisMetaV1DeleteOptions, IoK8sApimachineryPkgApisMetaV1Preconditions, ModelCircle, ModelFromZendesk, ModelSquare, ModelThatExtends, ModelThatExtendsExtends, ModelWithAdditionalPropertiesEqTrue, ModelWithAdditionalPropertiesRef, ModelWithAnyOfConstantSizeArray, ModelWithAnyOfConstantSizeArrayAndIntersect, ModelWithAnyOfConstantSizeArrayNullable, ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions, ModelWithAnyOfConstantSizeArrayWithNSizeAndOptionsWritable, ModelWithArray, ModelWithArrayReadOnlyAndWriteOnly, ModelWithArrayReadOnlyAndWriteOnlyWritable, ModelWithBackticksInDescription, ModelWithBoolean, ModelWithCircularReference, ModelWithConst, ModelWithConstantSizeArray, ModelWithDictionary, ModelWithDuplicateImports, ModelWithDuplicateProperties, ModelWithEnum, ModelWithEnumFromDescription, ModelWithEnumWithHyphen, ModelWithInteger, ModelWithNestedArrayEnums, ModelWithNestedArrayEnumsData, ModelWithNestedArrayEnumsDataBar, ModelWithNestedArrayEnumsDataFoo, ModelWithNestedCompositionEnums, ModelWithNestedEnums, ModelWithNestedProperties, ModelWithNullableObject, ModelWithNullableString, ModelWithNumericEnumUnion, ModelWithOneOfAndProperties, ModelWithOneOfEnum, ModelWithOrderedProperties, ModelWithPattern, ModelWithPatternWritable, ModelWithPrefixItemsConstantSizeArray, ModelWithProperties, ModelWithPropertiesWritable, ModelWithReadOnlyAndWriteOnly, ModelWithReadOnlyAndWriteOnlyWritable, ModelWithReference, ModelWithReferenceWritable, ModelWithString, ModelWithStringError, MultipartRequestData, MultipartResponseData, MultipartResponseResponse, MultipartResponseResponses, NestedAnyOfArraysNullable, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, NonAsciiæøåÆøÅöôêÊ字符串Responses, NonAsciiStringæøåÆøÅöôêÊ字符串, NullableObject, OneOfAllOfIssue, OneOfAllOfIssueWritable, OptionsCallWithoutParametersAndResponseData, Pageable, ParameterSimpleParameterUnused, PatchApiVbyApiVersionNoTagData, PatchApiVbyApiVersionNoTagResponses, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionFormDataData, PostApiVbyApiVersionRequestBodyData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponse, PostCallWithOptionalParamResponses, PostCallWithoutParametersAndResponseData, PostServiceWithEmptyTagResponse, PostServiceWithEmptyTagResponse2, PutCallWithoutParametersAndResponseData, PutWithFormUrlEncodedData, SchemaWithFormRestrictedKeys, SimpleBoolean, SimpleFile, SimpleFormData, SimpleInteger, SimpleParameter, SimpleReference, SimpleRequestBody, SimpleString, SimpleStringWithPattern, TestErrorCodeData, TestErrorCodeErrors, TestErrorCodeResponses, TypesData, TypesResponse, TypesResponses, UploadFileData, UploadFileResponse, UploadFileResponses, XFooBar } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/msw.gen.ts new file mode 100644 index 0000000000..32b71d0727 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/msw.gen.ts @@ -0,0 +1,208 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { ApiVVersionODataControllerCountResponses, CallWithDuplicateResponsesResponses, CallWithNoContentResponseResponses, CallWithParametersData, CallWithResponseAndNoContentResponseResponses, CallWithResponsesResponses, CallWithResultFromHeaderResponses, CallWithWeirdParameterNamesData, ComplexParamsData, ComplexParamsResponses, ComplexTypesResponses, DeleteFooData3, DummyAResponses, DummyBResponses, FileResponseData, FileResponseResponses, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationResponses, GetCallWithOptionalParamData, ImportData, ImportResponses, MultipartRequestData, MultipartResponseResponses, NonAsciiæøåÆøÅöôêÊ字符串Responses, PostApiVbyApiVersionFormDataData, PostApiVbyApiVersionRequestBodyData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponses, PutWithFormUrlEncodedData, TestErrorCodeResponses, TypesData, TypesResponses, UploadFileData, UploadFileResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type OptionalHttpHandlerFactory = (responseOrResolver?: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +type StringifyPathParams = { + [K in keyof T]-?: string | ReadonlyArray; +}; + +export type SingleHandlerFactories = { + export: OptionalHttpHandlerFactory; + patchApiVbyApiVersionNoTag: OptionalHttpHandlerFactory; + import: HttpHandlerFactory | HttpResponseResolver>; + fooWow: OptionalHttpHandlerFactory; + apiVVersionODataControllerCount: HttpHandlerFactory | HttpResponseResolver>; + getApiVbyApiVersionSimpleOperation: HttpHandlerFactory | HttpResponseResolver>, never>>; + deleteCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + getCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + headCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + optionsCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + patchCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + postCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + putCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + deleteFoo: OptionalHttpHandlerFactory>, never>>; + callWithDescriptions: OptionalHttpHandlerFactory; + deprecatedCall: OptionalHttpHandlerFactory; + callWithParameters: OptionalHttpHandlerFactory>, CallWithParametersData['body']>>; + callWithWeirdParameterNames: OptionalHttpHandlerFactory>, CallWithWeirdParameterNamesData['body']>>; + getCallWithOptionalParam: OptionalHttpHandlerFactory>; + postCallWithOptionalParam: HttpHandlerFactory | HttpResponseResolver>; + postApiVbyApiVersionRequestBody: OptionalHttpHandlerFactory>; + postApiVbyApiVersionFormData: OptionalHttpHandlerFactory>; + callWithDefaultParameters: OptionalHttpHandlerFactory; + callWithDefaultOptionalParameters: OptionalHttpHandlerFactory; + callToTestOrderOfParams: OptionalHttpHandlerFactory; + duplicateName: OptionalHttpHandlerFactory; + duplicateName2: OptionalHttpHandlerFactory; + duplicateName3: OptionalHttpHandlerFactory; + duplicateName4: OptionalHttpHandlerFactory; + callWithNoContentResponse: OptionalHttpHandlerFactory | HttpResponseResolver>; + callWithResponseAndNoContentResponse: HttpHandlerFactory | HttpResponseResolver>; + dummyA: HttpHandlerFactory | HttpResponseResolver>; + dummyB: OptionalHttpHandlerFactory | HttpResponseResolver>; + callWithResponse: OptionalHttpHandlerFactory; + callWithDuplicateResponses: HttpHandlerFactory | HttpResponseResolver>; + callWithResponses: HttpHandlerFactory | HttpResponseResolver>; + collectionFormat: OptionalHttpHandlerFactory; + types: HttpHandlerFactory | HttpResponseResolver>, never>>; + uploadFile: HttpHandlerFactory | HttpResponseResolver>, UploadFileData['body']>>; + fileResponse: HttpHandlerFactory | HttpResponseResolver>, never>>; + complexTypes: HttpHandlerFactory | HttpResponseResolver>; + multipartResponse: HttpHandlerFactory | HttpResponseResolver>; + multipartRequest: OptionalHttpHandlerFactory>; + complexParams: HttpHandlerFactory | HttpResponseResolver>, ComplexParamsData['body']>>; + callWithResultFromHeader: OptionalHttpHandlerFactory | HttpResponseResolver>; + testErrorCode: OptionalHttpHandlerFactory | HttpResponseResolver>; + nonAsciiæøåÆøÅöôêÊ字符串: HttpHandlerFactory | HttpResponseResolver>; + putWithFormUrlEncoded: OptionalHttpHandlerFactory>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? 'http://localhost:3000/base'; + const of: SingleHandlerFactories = { + export: (res, options) => http.get(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + patchApiVbyApiVersionNoTag: (res, options) => http.patch(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + import: (res, options) => http.post(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + fooWow: (res, options) => http.put(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + apiVVersionODataControllerCount: (res, options) => http.get(toMswPath('/api/v{api-version}/simple/$count', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + getApiVbyApiVersionSimpleOperation: (res, options) => http.get(toMswPath('/api/v{api-version}/simple:operation', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + deleteCallWithoutParametersAndResponse: (res, options) => http.delete(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + getCallWithoutParametersAndResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + headCallWithoutParametersAndResponse: (res, options) => http.head(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + optionsCallWithoutParametersAndResponse: (res, options) => http.options(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + patchCallWithoutParametersAndResponse: (res, options) => http.patch(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + postCallWithoutParametersAndResponse: (res, options) => http.post(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + putCallWithoutParametersAndResponse: (res, options) => http.put(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + deleteFoo: (res, options) => http.delete(toMswPath('/api/v{api-version}/foo/{foo_param}/bar/{BarParam}', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDescriptions: (res, options) => http.post(toMswPath('/api/v{api-version}/descriptions', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + deprecatedCall: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters/deprecated', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithParameters: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters/{parameterPath}', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithWeirdParameterNames: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + getCallWithOptionalParam: (res, options) => http.get(toMswPath('/api/v{api-version}/parameters', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + postCallWithOptionalParam: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postApiVbyApiVersionRequestBody: (res, options) => http.post(toMswPath('/api/v{api-version}/requestBody', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + postApiVbyApiVersionFormData: (res, options) => http.post(toMswPath('/api/v{api-version}/formData', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDefaultParameters: (res, options) => http.get(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDefaultOptionalParameters: (res, options) => http.post(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callToTestOrderOfParams: (res, options) => http.put(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName: (res, options) => http.delete(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName2: (res, options) => http.get(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName3: (res, options) => http.post(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName4: (res, options) => http.put(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithNoContentResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/no-content', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponseAndNoContentResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/response-and-no-content', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + dummyA: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/a', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + dummyB: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/b', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDuplicateResponses: (res, options) => http.post(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponses: (res, options) => http.put(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + collectionFormat: (res, options) => http.get(toMswPath('/api/v{api-version}/collectionFormat', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + types: (res, options) => http.get(toMswPath('/api/v{api-version}/types', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + uploadFile: (res, options) => http.post(toMswPath('/api/v{api-version}/upload', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + fileResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/file/{id}', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + complexTypes: (res, options) => http.get(toMswPath('/api/v{api-version}/complex', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + multipartResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/multipart', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + multipartRequest: (res, options) => http.post(toMswPath('/api/v{api-version}/multipart', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + complexParams: (res, options) => http.put(toMswPath('/api/v{api-version}/complex/{id}', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResultFromHeader: (res, options) => http.post(toMswPath('/api/v{api-version}/header', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + testErrorCode: (res, options) => http.post(toMswPath('/api/v{api-version}/error', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + nonAsciiæøåÆøÅöôêÊ字符串: (res, options) => http.post(toMswPath('/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + putWithFormUrlEncoded: (res, options) => http.put(toMswPath('/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串', baseUrl), typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + handlers.push(of.export(overrides?.export)); + handlers.push(of.patchApiVbyApiVersionNoTag(overrides?.patchApiVbyApiVersionNoTag)); + addRequiredHandler(of.import, overrides?.import); + handlers.push(of.fooWow(overrides?.fooWow)); + addRequiredHandler(of.apiVVersionODataControllerCount, overrides?.apiVVersionODataControllerCount); + addRequiredHandler(of.getApiVbyApiVersionSimpleOperation, overrides?.getApiVbyApiVersionSimpleOperation); + handlers.push(of.deleteCallWithoutParametersAndResponse(overrides?.deleteCallWithoutParametersAndResponse)); + handlers.push(of.getCallWithoutParametersAndResponse(overrides?.getCallWithoutParametersAndResponse)); + handlers.push(of.headCallWithoutParametersAndResponse(overrides?.headCallWithoutParametersAndResponse)); + handlers.push(of.optionsCallWithoutParametersAndResponse(overrides?.optionsCallWithoutParametersAndResponse)); + handlers.push(of.patchCallWithoutParametersAndResponse(overrides?.patchCallWithoutParametersAndResponse)); + handlers.push(of.postCallWithoutParametersAndResponse(overrides?.postCallWithoutParametersAndResponse)); + handlers.push(of.putCallWithoutParametersAndResponse(overrides?.putCallWithoutParametersAndResponse)); + handlers.push(of.deleteFoo(overrides?.deleteFoo)); + handlers.push(of.callWithDescriptions(overrides?.callWithDescriptions)); + handlers.push(of.deprecatedCall(overrides?.deprecatedCall)); + handlers.push(of.callWithParameters(overrides?.callWithParameters)); + handlers.push(of.callWithWeirdParameterNames(overrides?.callWithWeirdParameterNames)); + handlers.push(of.getCallWithOptionalParam(overrides?.getCallWithOptionalParam)); + addRequiredHandler(of.postCallWithOptionalParam, overrides?.postCallWithOptionalParam); + handlers.push(of.postApiVbyApiVersionRequestBody(overrides?.postApiVbyApiVersionRequestBody)); + handlers.push(of.postApiVbyApiVersionFormData(overrides?.postApiVbyApiVersionFormData)); + handlers.push(of.callWithDefaultParameters(overrides?.callWithDefaultParameters)); + handlers.push(of.callWithDefaultOptionalParameters(overrides?.callWithDefaultOptionalParameters)); + handlers.push(of.callToTestOrderOfParams(overrides?.callToTestOrderOfParams)); + handlers.push(of.duplicateName(overrides?.duplicateName)); + handlers.push(of.duplicateName2(overrides?.duplicateName2)); + handlers.push(of.duplicateName3(overrides?.duplicateName3)); + handlers.push(of.duplicateName4(overrides?.duplicateName4)); + handlers.push(of.callWithNoContentResponse(overrides?.callWithNoContentResponse)); + addRequiredHandler(of.callWithResponseAndNoContentResponse, overrides?.callWithResponseAndNoContentResponse); + addRequiredHandler(of.dummyA, overrides?.dummyA); + handlers.push(of.dummyB(overrides?.dummyB)); + handlers.push(of.callWithResponse(overrides?.callWithResponse)); + addRequiredHandler(of.callWithDuplicateResponses, overrides?.callWithDuplicateResponses); + addRequiredHandler(of.callWithResponses, overrides?.callWithResponses); + handlers.push(of.collectionFormat(overrides?.collectionFormat)); + addRequiredHandler(of.types, overrides?.types); + addRequiredHandler(of.uploadFile, overrides?.uploadFile); + addRequiredHandler(of.fileResponse, overrides?.fileResponse); + addRequiredHandler(of.complexTypes, overrides?.complexTypes); + addRequiredHandler(of.multipartResponse, overrides?.multipartResponse); + handlers.push(of.multipartRequest(overrides?.multipartRequest)); + addRequiredHandler(of.complexParams, overrides?.complexParams); + handlers.push(of.callWithResultFromHeader(overrides?.callWithResultFromHeader)); + handlers.push(of.testErrorCode(overrides?.testErrorCode)); + addRequiredHandler(of.nonAsciiæøåÆøÅöôêÊ字符串, overrides?.nonAsciiæøåÆøÅöôêÊ字符串); + handlers.push(of.putWithFormUrlEncoded(overrides?.putWithFormUrlEncoded)); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/types.gen.ts new file mode 100644 index 0000000000..3287ee8737 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/default/types.gen.ts @@ -0,0 +1,2081 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: 'http://localhost:3000/base' | (string & {}); +}; + +/** + * Model with number-only name + */ +export type _400 = string; + +export type ExternalRefA = ExternalSharedModel; + +export type ExternalRefB = ExternalSharedModel; + +/** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ +export type CamelCaseCommentWithBreaks = number; + +/** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ +export type CommentWithBreaks = number; + +/** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ +export type CommentWithBackticks = number; + +/** + * Testing backticks and quotes in string: `backticks`, 'quotes', "double quotes" and ```multiple backticks``` should work + */ +export type CommentWithBackticksAndQuotes = number; + +/** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ +export type CommentWithSlashes = number; + +/** + * Testing expression placeholders in string: ${expression} should work + */ +export type CommentWithExpressionPlaceholders = number; + +/** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ +export type CommentWithQuotes = number; + +/** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ +export type CommentWithReservedCharacters = number; + +/** + * This is a simple number + */ +export type SimpleInteger = number; + +/** + * This is a simple boolean + */ +export type SimpleBoolean = boolean; + +/** + * This is a simple string + */ +export type SimpleString = string; + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export type NonAsciiStringæøåÆøÅöôêÊ字符串 = string; + +/** + * This is a simple file + */ +export type SimpleFile = Blob | File; + +/** + * This is a simple reference + */ +export type SimpleReference = ModelWithString; + +/** + * This is a simple string + */ +export type SimpleStringWithPattern = string | null; + +/** + * This is a simple enum with strings + */ +export type EnumWithStrings = 'Success' | 'Warning' | 'Error' | '\'Single Quote\'' | '"Double Quotes"' | 'Non-ascii: øæåôöØÆÅÔÖ字符串'; + +export type EnumWithReplacedCharacters = '\'Single Quote\'' | '"Double Quotes"' | 'øæåôöØÆÅÔÖ字符串' | 3.1 | ''; + +/** + * This is a simple enum with numbers + */ +export type EnumWithNumbers = 1 | 2 | 3 | 1.1 | 1.2 | 1.3 | 100 | 200 | 300 | -100 | -200 | -300 | -1.1 | -1.2 | -1.3; + +/** + * Success=1,Warning=2,Error=3 + */ +export type EnumFromDescription = number; + +/** + * This is a simple enum with numbers + */ +export type EnumWithExtensions = 200 | 400 | 500; + +export type EnumWithXEnumNames = 0 | 1 | 2; + +/** + * This is a simple array with numbers + */ +export type ArrayWithNumbers = Array; + +/** + * This is a simple array with booleans + */ +export type ArrayWithBooleans = Array; + +/** + * This is a simple array with strings + */ +export type ArrayWithStrings = Array; + +/** + * This is a simple array with references + */ +export type ArrayWithReferences = Array; + +/** + * This is a simple array containing an array + */ +export type ArrayWithArray = Array>; + +/** + * This is a simple array with properties + */ +export type ArrayWithProperties = Array<{ + '16x16'?: CamelCaseCommentWithBreaks; + bar?: string; +}>; + +/** + * This is a simple array with any of properties + */ +export type ArrayWithAnyOfProperties = Array<{ + foo?: string; +} | { + bar?: string; +}>; + +export type AnyOfAnyAndNull = { + data?: unknown; +}; + +/** + * This is a simple array with any of properties + */ +export type AnyOfArrays = { + results?: Array<{ + foo?: string; + } | { + bar?: string; + }>; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithString = { + [key: string]: string; +}; + +export type DictionaryWithPropertiesAndAdditionalProperties = { + foo?: number; + bar?: boolean; + [key: string]: string | number | boolean | undefined; +}; + +/** + * This is a string reference + */ +export type DictionaryWithReference = { + [key: string]: ModelWithString; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithArray = { + [key: string]: Array; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithDictionary = { + [key: string]: { + [key: string]: string; + }; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithProperties = { + [key: string]: { + foo?: string; + bar?: string; + }; +}; + +/** + * This is a model with one number property + */ +export type ModelWithInteger = { + /** + * This is a simple number property + */ + prop?: number; +}; + +/** + * This is a model with one boolean property + */ +export type ModelWithBoolean = { + /** + * This is a simple boolean property + */ + prop?: boolean; +}; + +/** + * This is a model with one string property + */ +export type ModelWithString = { + /** + * This is a simple string property + */ + prop?: string; +}; + +/** + * This is a model with one string property + */ +export type ModelWithStringError = { + /** + * This is a simple string property + */ + prop?: string; +}; + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export type ModelFromZendesk = string; + +/** + * This is a model with one string property + */ +export type ModelWithNullableString = { + /** + * This is a simple string property + */ + nullableProp1?: string | null; + /** + * This is a simple string property + */ + nullableRequiredProp1: string | null; + /** + * This is a simple string property + */ + nullableProp2?: string | null; + /** + * This is a simple string property + */ + nullableRequiredProp2: string | null; + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; +}; + +/** + * This is a model with one enum + */ +export type ModelWithEnum = { + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; + /** + * These are the HTTP error code enums + */ + statusCode?: '100' | '200 FOO' | '300 FOO_BAR' | '400 foo-bar' | '500 foo.bar' | '600 foo&bar'; + /** + * Simple boolean enum + */ + bool?: true; +}; + +/** + * This is a model with one enum with escaped name + */ +export type ModelWithEnumWithHyphen = { + /** + * Foo-Bar-Baz-Qux + */ + 'foo-bar-baz-qux'?: '3.0'; +}; + +/** + * This is a model with one enum + */ +export type ModelWithEnumFromDescription = { + /** + * Success=1,Warning=2,Error=3 + */ + test?: number; +}; + +/** + * This is a model with nested enums + */ +export type ModelWithNestedEnums = { + dictionaryWithEnum?: { + [key: string]: 'Success' | 'Warning' | 'Error'; + }; + dictionaryWithEnumFromDescription?: { + [key: string]: number; + }; + arrayWithEnum?: Array<'Success' | 'Warning' | 'Error'>; + arrayWithDescription?: Array; + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; +}; + +/** + * This is a model with one property containing a reference + */ +export type ModelWithReference = { + prop?: ModelWithProperties; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArrayReadOnlyAndWriteOnly = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArray = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing a dictionary + */ +export type ModelWithDictionary = { + prop?: { + [key: string]: string; + }; +}; + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export type DeprecatedModel = { + /** + * This is a deprecated property + * + * @deprecated + */ + prop?: string; +}; + +/** + * This is a model with one property containing a circular reference + */ +export type ModelWithCircularReference = { + prop?: ModelWithCircularReference; +}; + +/** + * This is a model with one property with a 'one of' relationship + */ +export type CompositionWithOneOf = { + propA?: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; +}; + +/** + * This is a model with one property with a 'one of' relationship where the options are not $ref + */ +export type CompositionWithOneOfAnonymous = { + propA?: { + propA?: string; + } | string | number; +}; + +/** + * Circle + */ +export type ModelCircle = { + kind: string; + radius?: number; +}; + +/** + * Square + */ +export type ModelSquare = { + kind: string; + sideLength?: number; +}; + +/** + * This is a model with one property with a 'one of' relationship where the options are not $ref + */ +export type CompositionWithOneOfDiscriminator = ({ + kind: 'circle'; +} & ModelCircle) | ({ + kind: 'square'; +} & ModelSquare); + +/** + * This is a model with one property with a 'any of' relationship + */ +export type CompositionWithAnyOf = { + propA?: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; +}; + +/** + * This is a model with one property with a 'any of' relationship where the options are not $ref + */ +export type CompositionWithAnyOfAnonymous = { + propA?: { + propA?: string; + } | string | number; +}; + +/** + * This is a model with nested 'any of' property with a type null + */ +export type CompositionWithNestedAnyAndTypeNull = { + propA?: Array | Array; +}; + +export type _3eNum1Период = 'Bird' | 'Dog'; + +export type ConstValue = 'ConstValue'; + +/** + * This is a model with one property with a 'any of' relationship where the options are not $ref + */ +export type CompositionWithNestedAnyOfAndNull = { + propA?: Array<_3eNum1Период | ConstValue> | null; +}; + +/** + * This is a model with one property with a 'one of' relationship + */ +export type CompositionWithOneOfAndNullable = { + propA?: { + boolean?: boolean; + } | ModelWithEnum | ModelWithArray | ModelWithDictionary | null; +}; + +/** + * This is a model that contains a simple dictionary within composition + */ +export type CompositionWithOneOfAndSimpleDictionary = { + propA?: boolean | { + [key: string]: number; + }; +}; + +/** + * This is a model that contains a dictionary of simple arrays within composition + */ +export type CompositionWithOneOfAndSimpleArrayDictionary = { + propA?: boolean | { + [key: string]: Array; + }; +}; + +/** + * This is a model that contains a dictionary of complex arrays (composited) within composition + */ +export type CompositionWithOneOfAndComplexArrayDictionary = { + propA?: boolean | { + [key: string]: Array; + }; +}; + +/** + * This is a model with one property with a 'all of' relationship + */ +export type CompositionWithAllOfAndNullable = { + propA?: ({ + boolean?: boolean; + } & ModelWithEnum & ModelWithArray & ModelWithDictionary) | null; +}; + +/** + * This is a model with one property with a 'any of' relationship + */ +export type CompositionWithAnyOfAndNullable = { + propA?: { + boolean?: boolean; + } | ModelWithEnum | ModelWithArray | ModelWithDictionary | null; +}; + +/** + * This is a base model with two simple optional properties + */ +export type CompositionBaseModel = { + firstName?: string; + lastname?: string; +}; + +/** + * This is a model that extends the base model + */ +export type CompositionExtendedModel = CompositionBaseModel & { + age: number; + firstName: string; + lastname: string; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithProperties = { + required: string; + readonly requiredAndReadOnly: string; + requiredAndNullable: string | null; + string?: string; + number?: number; + boolean?: boolean; + reference?: ModelWithString; + 'property with space'?: string; + default?: string; + try?: string; + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithNestedProperties = { + readonly first: { + readonly second: { + readonly third: string | null; + } | null; + } | null; +}; + +/** + * This is a model with duplicated properties + */ +export type ModelWithDuplicateProperties = { + prop?: ModelWithString; +}; + +/** + * This is a model with ordered properties + */ +export type ModelWithOrderedProperties = { + zebra?: string; + apple?: string; + hawaii?: string; +}; + +/** + * This is a model with duplicated imports + */ +export type ModelWithDuplicateImports = { + propA?: ModelWithString; + propB?: ModelWithString; + propC?: ModelWithString; +}; + +/** + * This is a model that extends another model + */ +export type ModelThatExtends = ModelWithString & { + propExtendsA?: string; + propExtendsB?: ModelWithString; +}; + +/** + * This is a model that extends another model + */ +export type ModelThatExtendsExtends = ModelWithString & ModelThatExtends & { + propExtendsC?: string; + propExtendsD?: ModelWithString; +}; + +/** + * This is a model that contains a some patterns + */ +export type ModelWithPattern = { + key: string; + name: string; + readonly enabled?: boolean; + readonly modified?: string; + id?: string; + text?: string; + patternWithSingleQuotes?: string; + patternWithNewline?: string; + patternWithBacktick?: string; + patternWithUnicode?: string; +}; + +export type File = { + /** + * Id + */ + readonly id?: string; + /** + * Updated at + */ + readonly updated_at?: string; + /** + * Created at + */ + readonly created_at?: string; + /** + * Mime + */ + mime: string; + /** + * File + */ + readonly file?: string; +}; + +export type Default = { + name?: string; +}; + +export type Pageable = { + page?: number; + size?: number; + sort?: Array; +}; + +/** + * This is a free-form object without additionalProperties. + */ +export type FreeFormObjectWithoutAdditionalProperties = { + [key: string]: unknown; +}; + +/** + * This is a free-form object with additionalProperties: true. + */ +export type FreeFormObjectWithAdditionalPropertiesEqTrue = { + [key: string]: unknown; +}; + +/** + * This is a free-form object with additionalProperties: {}. + */ +export type FreeFormObjectWithAdditionalPropertiesEqEmptyObject = { + [key: string]: unknown; +}; + +export type ModelWithConst = { + String?: 'String'; + number?: 0; + null?: unknown; + withType?: 'Some string'; +}; + +/** + * This is a model with one property and additionalProperties: true + */ +export type ModelWithAdditionalPropertiesEqTrue = { + /** + * This is a simple string property + */ + prop?: string; + [key: string]: unknown; +}; + +export type NestedAnyOfArraysNullable = { + nullableArray?: Array | null; +}; + +export type CompositionWithOneOfAndProperties = ({ + foo: SimpleParameter; +} | { + bar: NonAsciiStringæøåÆøÅöôêÊ字符串; +}) & { + baz: number | null; + qux: number; +}; + +/** + * An object that can be null + */ +export type NullableObject = { + foo?: string; +} | null; + +/** + * Some % character + */ +export type CharactersInDescription = string; + +export type ModelWithNullableObject = { + data?: NullableObject; +}; + +/** + * An object with additional properties that can be null + */ +export type ModelWithAdditionalPropertiesRef = { + [key: string]: NullableObject | null; +}; + +export type ModelWithOneOfEnum = { + foo: 'Bar'; +} | { + foo: 'Baz'; +} | { + foo: 'Qux'; +} | { + content: string; + foo: 'Quux'; +} | { + content: [ + string, + string + ]; + foo: 'Corge'; +}; + +export type ModelWithNestedArrayEnumsDataFoo = 'foo' | 'bar'; + +export type ModelWithNestedArrayEnumsDataBar = 'baz' | 'qux'; + +export type ModelWithNestedArrayEnumsData = { + foo?: Array; + bar?: Array; +}; + +export type ModelWithNestedArrayEnums = { + array_strings?: Array; + data?: ModelWithNestedArrayEnumsData; +}; + +export type ModelWithNestedCompositionEnums = { + foo?: ModelWithNestedArrayEnumsDataFoo; +}; + +export type ModelWithReadOnlyAndWriteOnly = { + foo: string; + readonly bar: string; +}; + +export type ModelWithConstantSizeArray = [ + number, + number +]; + +export type ModelWithAnyOfConstantSizeArray = [ + number | string, + number | string, + number | string +]; + +export type ModelWithPrefixItemsConstantSizeArray = Array; + +export type ModelWithAnyOfConstantSizeArrayNullable = [ + number | null | string, + number | null | string, + number | null | string +]; + +export type ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = [ + number | Import, + number | Import +]; + +export type ModelWithAnyOfConstantSizeArrayAndIntersect = [ + number & string, + number & string +]; + +export type ModelWithNumericEnumUnion = { + /** + * Период + */ + value?: -10 | -1 | 0 | 1 | 3 | 6 | 12; +}; + +/** + * Some description with `back ticks` + */ +export type ModelWithBackticksInDescription = { + /** + * The template `that` should be used for parsing and importing the contents of the CSV file. + * + *

There is one placeholder currently supported:

  • ${x} - refers to the n-th column in the CSV file, e.g. ${1}, ${2}, ...)

Example of a correct JSON template:

+ *
+     * [
+     * {
+     * "resourceType": "Asset",
+     * "identifier": {
+     * "name": "${1}",
+     * "domain": {
+     * "name": "${2}",
+     * "community": {
+     * "name": "Some Community"
+     * }
+     * }
+     * },
+     * "attributes" : {
+     * "00000000-0000-0000-0000-000000003115" : [ {
+     * "value" : "${3}"
+     * } ],
+     * "00000000-0000-0000-0000-000000000222" : [ {
+     * "value" : "${4}"
+     * } ]
+     * }
+     * }
+     * ]
+     * 
+ */ + template?: string; +}; + +export type ModelWithOneOfAndProperties = (SimpleParameter | NonAsciiStringæøåÆøÅöôêÊ字符串) & { + baz: number | null; + qux: number; +}; + +/** + * Model used to test deduplication strategy (unused) + */ +export type ParameterSimpleParameterUnused = string; + +/** + * Model used to test deduplication strategy + */ +export type PostServiceWithEmptyTagResponse = string; + +/** + * Model used to test deduplication strategy + */ +export type PostServiceWithEmptyTagResponse2 = string; + +/** + * Model used to test deduplication strategy + */ +export type DeleteFooData = string; + +/** + * Model used to test deduplication strategy + */ +export type DeleteFooData2 = string; + +/** + * Model with restricted keyword name + */ +export type Import = string; + +export type SchemaWithFormRestrictedKeys = { + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + object?: { + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + }; + array?: Array<{ + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + }>; +}; + +/** + * This schema was giving PascalCase transformations a hard time + */ +export type IoK8sApimachineryPkgApisMetaV1DeleteOptions = { + /** + * Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned. + */ + preconditions?: IoK8sApimachineryPkgApisMetaV1Preconditions; +}; + +/** + * This schema was giving PascalCase transformations a hard time + */ +export type IoK8sApimachineryPkgApisMetaV1Preconditions = { + /** + * Specifies the target ResourceVersion + */ + resourceVersion?: string; + /** + * Specifies the target UID. + */ + uid?: string; +}; + +export type AdditionalPropertiesUnknownIssue = { + [key: string]: string | number; +}; + +export type AdditionalPropertiesUnknownIssue2 = { + [key: string]: string | number; +}; + +export type AdditionalPropertiesUnknownIssue3 = string & { + entries: { + [key: string]: AdditionalPropertiesUnknownIssue; + }; +}; + +export type AdditionalPropertiesIntegerIssue = { + value: number; + [key: string]: number; +}; + +export type OneOfAllOfIssue = ((ConstValue | GenericSchemaDuplicateIssue1SystemBoolean) & _3eNum1Период) | GenericSchemaDuplicateIssue1SystemString; + +export type GenericSchemaDuplicateIssue1SystemBoolean = { + item?: boolean; + error?: string | null; + readonly hasError?: boolean; + data?: { + [key: string]: never; + }; +}; + +export type GenericSchemaDuplicateIssue1SystemString = { + item?: string | null; + error?: string | null; + readonly hasError?: boolean; +}; + +export type ExternalSharedModel = { + id: string; + name?: string; +}; + +/** + * This is a model with one property containing a reference + */ +export type ModelWithReferenceWritable = { + prop?: ModelWithPropertiesWritable; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArrayReadOnlyAndWriteOnlyWritable = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithPropertiesWritable = { + required: string; + requiredAndNullable: string | null; + string?: string; + number?: number; + boolean?: boolean; + reference?: ModelWithString; + 'property with space'?: string; + default?: string; + try?: string; +}; + +/** + * This is a model that contains a some patterns + */ +export type ModelWithPatternWritable = { + key: string; + name: string; + id?: string; + text?: string; + patternWithSingleQuotes?: string; + patternWithNewline?: string; + patternWithBacktick?: string; + patternWithUnicode?: string; +}; + +export type FileWritable = { + /** + * Mime + */ + mime: string; +}; + +export type ModelWithReadOnlyAndWriteOnlyWritable = { + foo: string; + baz: string; +}; + +export type ModelWithAnyOfConstantSizeArrayWithNSizeAndOptionsWritable = [ + number | Import, + number | Import +]; + +export type AdditionalPropertiesUnknownIssueWritable = { + [key: string]: string | number; +}; + +export type OneOfAllOfIssueWritable = ((ConstValue | GenericSchemaDuplicateIssue1SystemBoolean) & _3eNum1Период) | GenericSchemaDuplicateIssue1SystemString; + +export type GenericSchemaDuplicateIssue1SystemBooleanWritable = { + item?: boolean; + error?: string | null; + data?: { + [key: string]: never; + }; +}; + +export type GenericSchemaDuplicateIssue1SystemStringWritable = { + item?: string | null; + error?: string | null; +}; + +/** + * This is a reusable parameter + */ +export type SimpleParameter = string; + +/** + * Parameter with illegal characters + */ +export type XFooBar = ModelWithString; + +export type SimpleRequestBody = ModelWithString; + +export type SimpleFormData = ModelWithString; + +export type ExportData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type PatchApiVbyApiVersionNoTagData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type PatchApiVbyApiVersionNoTagResponses = { + /** + * OK + */ + default: unknown; +}; + +export type ImportData = { + body: ModelWithReadOnlyAndWriteOnlyWritable | ModelWithArrayReadOnlyAndWriteOnlyWritable; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type ImportResponses = { + /** + * Success + */ + 200: ModelFromZendesk; + /** + * Default success response + */ + default: ModelWithReadOnlyAndWriteOnly; +}; + +export type ImportResponse = ImportResponses[keyof ImportResponses]; + +export type FooWowData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type FooWowResponses = { + /** + * OK + */ + default: unknown; +}; + +export type ApiVVersionODataControllerCountData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple/$count'; +}; + +export type ApiVVersionODataControllerCountResponses = { + /** + * Success + */ + 200: ModelFromZendesk; +}; + +export type ApiVVersionODataControllerCountResponse = ApiVVersionODataControllerCountResponses[keyof ApiVVersionODataControllerCountResponses]; + +export type GetApiVbyApiVersionSimpleOperationData = { + body?: never; + path: { + /** + * foo in method + */ + foo_param: string; + }; + query?: never; + url: '/api/v{api-version}/simple:operation'; +}; + +export type GetApiVbyApiVersionSimpleOperationErrors = { + /** + * Default error response + */ + default: ModelWithBoolean; +}; + +export type GetApiVbyApiVersionSimpleOperationError = GetApiVbyApiVersionSimpleOperationErrors[keyof GetApiVbyApiVersionSimpleOperationErrors]; + +export type GetApiVbyApiVersionSimpleOperationResponses = { + /** + * Response is a simple number + */ + 200: number; +}; + +export type GetApiVbyApiVersionSimpleOperationResponse = GetApiVbyApiVersionSimpleOperationResponses[keyof GetApiVbyApiVersionSimpleOperationResponses]; + +export type DeleteCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type GetCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type HeadCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type OptionsCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PatchCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PostCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PutCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type DeleteFooData3 = { + body?: never; + headers: { + /** + * Parameter with illegal characters + */ + 'x-Foo-Bar': ModelWithString; + }; + path: { + /** + * foo in method + */ + foo_param: string; + /** + * bar in method + */ + BarParam: string; + }; + query?: never; + url: '/api/v{api-version}/foo/{foo_param}/bar/{BarParam}'; +}; + +export type CallWithDescriptionsData = { + body?: never; + path?: never; + query?: { + /** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ + parameterWithBreaks?: string; + /** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ + parameterWithBackticks?: string; + /** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ + parameterWithSlashes?: string; + /** + * Testing expression placeholders in string: ${expression} should work + */ + parameterWithExpressionPlaceholders?: string; + /** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ + parameterWithQuotes?: string; + /** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ + parameterWithReservedCharacters?: string; + }; + url: '/api/v{api-version}/descriptions'; +}; + +export type DeprecatedCallData = { + body?: never; + headers: { + /** + * This parameter is deprecated + * + * @deprecated + */ + parameter: DeprecatedModel | null; + }; + path?: never; + query?: never; + url: '/api/v{api-version}/parameters/deprecated'; +}; + +export type CallWithParametersData = { + /** + * This is the parameter that goes into the body + */ + body: { + [key: string]: unknown; + } | null; + headers: { + /** + * This is the parameter that goes into the header + */ + parameterHeader: string | null; + }; + path: { + /** + * This is the parameter that goes into the path + */ + parameterPath: string | null; + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query: { + foo_ref_enum?: ModelWithNestedArrayEnumsDataFoo; + foo_all_of_enum: ModelWithNestedArrayEnumsDataFoo; + /** + * This is the parameter that goes into the query params + */ + cursor: string | null; + }; + url: '/api/v{api-version}/parameters/{parameterPath}'; +}; + +export type CallWithWeirdParameterNamesData = { + /** + * This is the parameter that goes into the body + */ + body: ModelWithString | null; + headers: { + /** + * This is the parameter that goes into the request header + */ + 'parameter.header': string | null; + }; + path: { + /** + * This is the parameter that goes into the path + */ + 'parameter.path.1'?: string; + /** + * This is the parameter that goes into the path + */ + 'parameter-path-2'?: string; + /** + * This is the parameter that goes into the path + */ + 'PARAMETER-PATH-3'?: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query: { + /** + * This is the parameter with a reserved keyword + */ + default?: string; + /** + * This is the parameter that goes into the request query params + */ + 'parameter-query': string | null; + }; + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}'; +}; + +export type GetCallWithOptionalParamData = { + /** + * This is a required parameter + */ + body: ModelWithOneOfEnum; + path?: never; + query?: { + /** + * This is an optional parameter + */ + page?: number; + }; + url: '/api/v{api-version}/parameters'; +}; + +export type PostCallWithOptionalParamData = { + /** + * This is an optional parameter + */ + body?: { + offset?: number | null; + }; + path?: never; + query: { + /** + * This is a required parameter + */ + parameter: Pageable; + }; + url: '/api/v{api-version}/parameters'; +}; + +export type PostCallWithOptionalParamResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; +}; + +export type PostCallWithOptionalParamResponse = PostCallWithOptionalParamResponses[keyof PostCallWithOptionalParamResponses]; + +export type PostApiVbyApiVersionRequestBodyData = { + /** + * A reusable request body + */ + body?: SimpleRequestBody; + path?: never; + query?: { + /** + * This is a reusable parameter + */ + parameter?: string; + }; + url: '/api/v{api-version}/requestBody'; +}; + +export type PostApiVbyApiVersionFormDataData = { + /** + * A reusable request body + */ + body?: SimpleFormData; + path?: never; + query?: { + /** + * This is a reusable parameter + */ + parameter?: string; + }; + url: '/api/v{api-version}/formData'; +}; + +export type CallWithDefaultParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string with default value + */ + parameterString?: string | null; + /** + * This is a simple number with default value + */ + parameterNumber?: number | null; + /** + * This is a simple boolean with default value + */ + parameterBoolean?: boolean | null; + /** + * This is a simple enum with default value + */ + parameterEnum?: 'Success' | 'Warning' | 'Error'; + /** + * This is a simple model with default value + */ + parameterModel?: ModelWithString | null; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallWithDefaultOptionalParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string that is optional with default value + */ + parameterString?: string; + /** + * This is a simple number that is optional with default value + */ + parameterNumber?: number; + /** + * This is a simple boolean that is optional with default value + */ + parameterBoolean?: boolean; + /** + * This is a simple enum that is optional with default value + */ + parameterEnum?: 'Success' | 'Warning' | 'Error'; + /** + * This is a simple model that is optional with default value + */ + parameterModel?: ModelWithString; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallToTestOrderOfParamsData = { + body?: never; + path?: never; + query: { + /** + * This is a optional string with default + */ + parameterOptionalStringWithDefault?: string; + /** + * This is a optional string with empty default + */ + parameterOptionalStringWithEmptyDefault?: string; + /** + * This is a optional string with no default + */ + parameterOptionalStringWithNoDefault?: string; + /** + * This is a string with default + */ + parameterStringWithDefault: string; + /** + * This is a string with empty default + */ + parameterStringWithEmptyDefault: string; + /** + * This is a string with no default + */ + parameterStringWithNoDefault: string; + /** + * This is a string that can be null with no default + */ + parameterStringNullableWithNoDefault?: string | null; + /** + * This is a string that can be null with default + */ + parameterStringNullableWithDefault?: string | null; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type DuplicateNameData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName2Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName3Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName4Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type CallWithNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-content'; +}; + +export type CallWithNoContentResponseResponses = { + /** + * Success + */ + 204: void; +}; + +export type CallWithNoContentResponseResponse = CallWithNoContentResponseResponses[keyof CallWithNoContentResponseResponses]; + +export type CallWithResponseAndNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/response-and-no-content'; +}; + +export type CallWithResponseAndNoContentResponseResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; +}; + +export type CallWithResponseAndNoContentResponseResponse = CallWithResponseAndNoContentResponseResponses[keyof CallWithResponseAndNoContentResponseResponses]; + +export type DummyAData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/a'; +}; + +export type DummyAResponses = { + 200: _400; +}; + +export type DummyAResponse = DummyAResponses[keyof DummyAResponses]; + +export type DummyBData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/b'; +}; + +export type DummyBResponses = { + /** + * Success + */ + 204: void; +}; + +export type DummyBResponse = DummyBResponses[keyof DummyBResponses]; + +export type CallWithResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponseResponses = { + default: Import; +}; + +export type CallWithResponseResponse = CallWithResponseResponses[keyof CallWithResponseResponses]; + +export type CallWithDuplicateResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithDuplicateResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for 4XX errors + */ + '4XX': DictionaryWithArray; + /** + * Default error response + */ + default: ModelWithBoolean; +}; + +export type CallWithDuplicateResponsesError = CallWithDuplicateResponsesErrors[keyof CallWithDuplicateResponsesErrors]; + +export type CallWithDuplicateResponsesResponses = { + /** + * Message for 200 response + */ + 200: ModelWithBoolean & ModelWithInteger; + /** + * Message for 201 response + */ + 201: ModelWithString; + /** + * Message for 202 response + */ + 202: ModelWithString; +}; + +export type CallWithDuplicateResponsesResponse = CallWithDuplicateResponsesResponses[keyof CallWithDuplicateResponsesResponses]; + +export type CallWithResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for default response + */ + default: ModelWithStringError; +}; + +export type CallWithResponsesError = CallWithResponsesErrors[keyof CallWithResponsesErrors]; + +export type CallWithResponsesResponses = { + /** + * Message for 200 response + */ + 200: { + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; + readonly value?: Array; + }; + /** + * Message for 201 response + */ + 201: ModelThatExtends; + /** + * Message for 202 response + */ + 202: ModelThatExtendsExtends; +}; + +export type CallWithResponsesResponse = CallWithResponsesResponses[keyof CallWithResponsesResponses]; + +export type CollectionFormatData = { + body?: never; + path?: never; + query: { + /** + * This is an array parameter that is sent as csv format (comma-separated values) + */ + parameterArrayCSV: Array | null; + /** + * This is an array parameter that is sent as ssv format (space-separated values) + */ + parameterArraySSV: Array | null; + /** + * This is an array parameter that is sent as tsv format (tab-separated values) + */ + parameterArrayTSV: Array | null; + /** + * This is an array parameter that is sent as pipes format (pipe-separated values) + */ + parameterArrayPipes: Array | null; + /** + * This is an array parameter that is sent as multi format (multiple parameter instances) + */ + parameterArrayMulti: Array | null; + }; + url: '/api/v{api-version}/collectionFormat'; +}; + +export type TypesData = { + body?: never; + path?: { + /** + * This is a number parameter + */ + id?: number; + }; + query: { + /** + * This is a number parameter + */ + parameterNumber: number; + /** + * This is a string parameter + */ + parameterString: string | null; + /** + * This is a boolean parameter + */ + parameterBoolean: boolean | null; + /** + * This is an object parameter + */ + parameterObject: { + [key: string]: unknown; + } | null; + /** + * This is an array parameter + */ + parameterArray: Array | null; + /** + * This is a dictionary parameter + */ + parameterDictionary: { + [key: string]: unknown; + } | null; + /** + * This is an enum parameter + */ + parameterEnum: 'Success' | 'Warning' | 'Error'; + }; + url: '/api/v{api-version}/types'; +}; + +export type TypesResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Response is a simple string + */ + 201: string; + /** + * Response is a simple boolean + */ + 202: boolean; + /** + * Response is a simple object + */ + 203: { + [key: string]: unknown; + }; +}; + +export type TypesResponse = TypesResponses[keyof TypesResponses]; + +export type UploadFileData = { + body: Blob | File; + path: { + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query?: never; + url: '/api/v{api-version}/upload'; +}; + +export type UploadFileResponses = { + 200: boolean; +}; + +export type UploadFileResponse = UploadFileResponses[keyof UploadFileResponses]; + +export type FileResponseData = { + body?: never; + path: { + id: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query?: never; + url: '/api/v{api-version}/file/{id}'; +}; + +export type FileResponseResponses = { + /** + * Success + */ + 200: Blob | File; +}; + +export type FileResponseResponse = FileResponseResponses[keyof FileResponseResponses]; + +export type ComplexTypesData = { + body?: never; + path?: never; + query: { + /** + * Parameter containing object + */ + parameterObject: { + first?: { + second?: { + third?: string; + }; + }; + }; + /** + * Parameter containing reference + */ + parameterReference: ModelWithString; + }; + url: '/api/v{api-version}/complex'; +}; + +export type ComplexTypesErrors = { + /** + * 400 `server` error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type ComplexTypesResponses = { + /** + * Successful response + */ + 200: Array; +}; + +export type ComplexTypesResponse = ComplexTypesResponses[keyof ComplexTypesResponses]; + +export type MultipartResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multipart'; +}; + +export type MultipartResponseResponses = { + /** + * OK + */ + 200: { + file?: Blob | File; + metadata?: { + foo?: string; + bar?: string; + }; + }; +}; + +export type MultipartResponseResponse = MultipartResponseResponses[keyof MultipartResponseResponses]; + +export type MultipartRequestData = { + body?: { + content?: Blob | File; + data?: ModelWithString | null; + }; + path?: never; + query?: never; + url: '/api/v{api-version}/multipart'; +}; + +export type ComplexParamsData = { + body?: { + readonly key: string | null; + name: string | null; + enabled?: boolean; + type: 'Monkey' | 'Horse' | 'Bird'; + listOfModels?: Array | null; + listOfStrings?: Array | null; + parameters: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; + readonly user?: { + readonly id?: number; + readonly name?: string | null; + }; + }; + path: { + id: number; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query?: never; + url: '/api/v{api-version}/complex/{id}'; +}; + +export type ComplexParamsResponses = { + /** + * Success + */ + 200: ModelWithString; +}; + +export type ComplexParamsResponse = ComplexParamsResponses[keyof ComplexParamsResponses]; + +export type CallWithResultFromHeaderData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/header'; +}; + +export type CallWithResultFromHeaderErrors = { + /** + * 400 server error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type CallWithResultFromHeaderResponses = { + /** + * Successful response + */ + 200: unknown; +}; + +export type TestErrorCodeData = { + body?: never; + path?: never; + query: { + /** + * Status code to return + */ + status: number; + }; + url: '/api/v{api-version}/error'; +}; + +export type TestErrorCodeErrors = { + /** + * Custom message: Internal Server Error + */ + 500: unknown; + /** + * Custom message: Not Implemented + */ + 501: unknown; + /** + * Custom message: Bad Gateway + */ + 502: unknown; + /** + * Custom message: Service Unavailable + */ + 503: unknown; +}; + +export type TestErrorCodeResponses = { + /** + * Custom message: Successful response + */ + 200: unknown; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Data = { + body?: never; + path?: never; + query: { + /** + * Dummy input param + */ + nonAsciiParamæøåÆØÅöôêÊ: number; + }; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Responses = { + /** + * Successful response + */ + 200: Array; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Response = NonAsciiæøåÆøÅöôêÊ字符串Responses[keyof NonAsciiæøåÆøÅöôêÊ字符串Responses]; + +export type PutWithFormUrlEncodedData = { + body: ArrayWithStrings; + path?: never; + query?: never; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/index.ts new file mode 100644 index 0000000000..b545604fba --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses, Person, PostFooData, PostFooResponse, PostFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/msw.gen.ts new file mode 100644 index 0000000000..096bd2386d --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/msw.gen.ts @@ -0,0 +1,64 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses, PostFooData, PostFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: HttpHandlerFactory | HttpResponseResolver>; + postFoo: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? ''; + const of: SingleHandlerFactories = { + getFoo: (res, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postFoo: (res, options) => http.post(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + addRequiredHandler(of.getFoo, overrides?.getFoo); + addRequiredHandler(of.postFoo, overrides?.postFoo); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/types.gen.ts new file mode 100644 index 0000000000..9d0535a2e7 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example-disabled/types.gen.ts @@ -0,0 +1,50 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; + +export type Person = { + firstName?: string; + lastName?: string; + age?: number; +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: Person; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; + +export type PostFooData = { + body: string; + path?: never; + query?: never; + url: '/foo'; +}; + +export type PostFooResponses = { + /** + * OK + */ + 200: { + fullName?: string; + age?: number; + }; + /** + * SUCCESSFUL + */ + 204: void; +}; + +export type PostFooResponse = PostFooResponses[keyof PostFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/index.ts new file mode 100644 index 0000000000..b545604fba --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses, Person, PostFooData, PostFooResponse, PostFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/msw.gen.ts new file mode 100644 index 0000000000..813ee85ef2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/msw.gen.ts @@ -0,0 +1,70 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses, PostFooData, PostFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type OptionalHttpHandlerFactory = (responseOrResolver?: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: OptionalHttpHandlerFactory | HttpResponseResolver>; + postFoo: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? ''; + const of: SingleHandlerFactories = { + getFoo: (res = { result: { + firstName: 'Marry', + lastName: 'Jane', + age: 30 + }, status: 200 }, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postFoo: (res, options) => http.post(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + handlers.push(of.getFoo(overrides?.getFoo)); + addRequiredHandler(of.postFoo, overrides?.postFoo); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/types.gen.ts new file mode 100644 index 0000000000..9d0535a2e7 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-example/types.gen.ts @@ -0,0 +1,50 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; + +export type Person = { + firstName?: string; + lastName?: string; + age?: number; +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: Person; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; + +export type PostFooData = { + body: string; + path?: never; + query?: never; + url: '/foo'; +}; + +export type PostFooResponses = { + /** + * OK + */ + 200: { + fullName?: string; + age?: number; + }; + /** + * SUCCESSFUL + */ + 204: void; +}; + +export type PostFooResponse = PostFooResponses[keyof PostFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/index.ts new file mode 100644 index 0000000000..f389f85547 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/msw.gen.ts new file mode 100644 index 0000000000..3073e3b33b --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/msw.gen.ts @@ -0,0 +1,61 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? ''; + const of: SingleHandlerFactories = { + getFoo: (res, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + addRequiredHandler(of.getFoo, overrides?.getFoo); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/types.gen.ts new file mode 100644 index 0000000000..a0b37c70a4 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/response-types/types.gen.ts @@ -0,0 +1,23 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: { + name?: string; + }; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/index.ts new file mode 100644 index 0000000000..f389f85547 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/msw.gen.ts new file mode 100644 index 0000000000..59e8e0c5fb --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/msw.gen.ts @@ -0,0 +1,61 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? 'https://foo.com/v1'; + const of: SingleHandlerFactories = { + getFoo: (res, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + addRequiredHandler(of.getFoo, overrides?.getFoo); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/types.gen.ts new file mode 100644 index 0000000000..d7a0ac4610 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/msw/servers/types.gen.ts @@ -0,0 +1,21 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: 'https://foo.com/v1' | `${string}://${string}/v1` | (string & {}); +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: string; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/index.ts new file mode 100644 index 0000000000..2ef59c1f0c --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { _3eNum1Период, _400, AdditionalPropertiesIntegerIssue, AdditionalPropertiesUnknownIssue, AdditionalPropertiesUnknownIssue2, AdditionalPropertiesUnknownIssue3, AdditionalPropertiesUnknownIssueWritable, AnyOfAnyAndNull, AnyOfArrays, ApiVVersionODataControllerCountData, ApiVVersionODataControllerCountResponse, ApiVVersionODataControllerCountResponses, ArrayWithAnyOfProperties, ArrayWithArray, ArrayWithBooleans, ArrayWithNumbers, ArrayWithProperties, ArrayWithReferences, ArrayWithStrings, CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesErrors, CallWithDuplicateResponsesResponse, CallWithDuplicateResponsesResponses, CallWithNoContentResponseData, CallWithNoContentResponseResponse, CallWithNoContentResponseResponses, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponse, CallWithResponseAndNoContentResponseResponses, CallWithResponseData, CallWithResponseResponse, CallWithResponseResponses, CallWithResponsesData, CallWithResponsesError, CallWithResponsesErrors, CallWithResponsesResponse, CallWithResponsesResponses, CallWithResultFromHeaderData, CallWithResultFromHeaderErrors, CallWithResultFromHeaderResponses, CallWithWeirdParameterNamesData, CamelCaseCommentWithBreaks, CharactersInDescription, ClientOptions, CollectionFormatData, CommentWithBackticks, CommentWithBackticksAndQuotes, CommentWithBreaks, CommentWithExpressionPlaceholders, CommentWithQuotes, CommentWithReservedCharacters, CommentWithSlashes, ComplexParamsData, ComplexParamsResponse, ComplexParamsResponses, ComplexTypesData, ComplexTypesErrors, ComplexTypesResponse, ComplexTypesResponses, CompositionBaseModel, CompositionExtendedModel, CompositionWithAllOfAndNullable, CompositionWithAnyOf, CompositionWithAnyOfAndNullable, CompositionWithAnyOfAnonymous, CompositionWithNestedAnyAndTypeNull, CompositionWithNestedAnyOfAndNull, CompositionWithOneOf, CompositionWithOneOfAndComplexArrayDictionary, CompositionWithOneOfAndNullable, CompositionWithOneOfAndProperties, CompositionWithOneOfAndSimpleArrayDictionary, CompositionWithOneOfAndSimpleDictionary, CompositionWithOneOfAnonymous, CompositionWithOneOfDiscriminator, ConstValue, Default, DeleteCallWithoutParametersAndResponseData, DeleteFooData, DeleteFooData2, DeleteFooData3, DeprecatedCallData, DeprecatedModel, DictionaryWithArray, DictionaryWithDictionary, DictionaryWithProperties, DictionaryWithPropertiesAndAdditionalProperties, DictionaryWithReference, DictionaryWithString, DummyAData, DummyAResponse, DummyAResponses, DummyBData, DummyBResponse, DummyBResponses, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, EnumFromDescription, EnumWithExtensions, EnumWithNumbers, EnumWithReplacedCharacters, EnumWithStrings, EnumWithXEnumNames, ExportData, ExternalRefA, ExternalRefB, ExternalSharedModel, File, FileResponseData, FileResponseResponse, FileResponseResponses, FileWritable, FooWowData, FooWowResponses, FreeFormObjectWithAdditionalPropertiesEqEmptyObject, FreeFormObjectWithAdditionalPropertiesEqTrue, FreeFormObjectWithoutAdditionalProperties, GenericSchemaDuplicateIssue1SystemBoolean, GenericSchemaDuplicateIssue1SystemBooleanWritable, GenericSchemaDuplicateIssue1SystemString, GenericSchemaDuplicateIssue1SystemStringWritable, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationError, GetApiVbyApiVersionSimpleOperationErrors, GetApiVbyApiVersionSimpleOperationResponse, GetApiVbyApiVersionSimpleOperationResponses, GetCallWithOptionalParamData, GetCallWithoutParametersAndResponseData, HeadCallWithoutParametersAndResponseData, Import, ImportData, ImportResponse, ImportResponses, IoK8sApimachineryPkgApisMetaV1DeleteOptions, IoK8sApimachineryPkgApisMetaV1Preconditions, ModelCircle, ModelFromZendesk, ModelSquare, ModelThatExtends, ModelThatExtendsExtends, ModelWithAdditionalPropertiesEqTrue, ModelWithAdditionalPropertiesRef, ModelWithAnyOfConstantSizeArray, ModelWithAnyOfConstantSizeArrayAndIntersect, ModelWithAnyOfConstantSizeArrayNullable, ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions, ModelWithAnyOfConstantSizeArrayWithNSizeAndOptionsWritable, ModelWithArray, ModelWithArrayReadOnlyAndWriteOnly, ModelWithArrayReadOnlyAndWriteOnlyWritable, ModelWithBackticksInDescription, ModelWithBoolean, ModelWithCircularReference, ModelWithConst, ModelWithConstantSizeArray, ModelWithDictionary, ModelWithDuplicateImports, ModelWithDuplicateProperties, ModelWithEnum, ModelWithEnumFromDescription, ModelWithEnumWithHyphen, ModelWithInteger, ModelWithNestedArrayEnums, ModelWithNestedArrayEnumsData, ModelWithNestedArrayEnumsDataBar, ModelWithNestedArrayEnumsDataFoo, ModelWithNestedCompositionEnums, ModelWithNestedEnums, ModelWithNestedProperties, ModelWithNullableObject, ModelWithNullableString, ModelWithNumericEnumUnion, ModelWithOneOfAndProperties, ModelWithOneOfEnum, ModelWithOrderedProperties, ModelWithPattern, ModelWithPatternWritable, ModelWithPrefixItemsConstantSizeArray, ModelWithProperties, ModelWithPropertiesWritable, ModelWithReadOnlyAndWriteOnly, ModelWithReadOnlyAndWriteOnlyWritable, ModelWithReference, ModelWithReferenceWritable, ModelWithString, ModelWithStringError, MultipartRequestData, MultipartResponseData, MultipartResponseResponse, MultipartResponseResponses, NestedAnyOfArraysNullable, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, NonAsciiæøåÆøÅöôêÊ字符串Responses, NonAsciiStringæøåÆøÅöôêÊ字符串, NullableObject, OneOfAllOfIssue, OneOfAllOfIssueWritable, OptionsCallWithoutParametersAndResponseData, Pageable, ParameterSimpleParameterUnused, PatchApiVbyApiVersionNoTagData, PatchApiVbyApiVersionNoTagResponses, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionFormDataData, PostApiVbyApiVersionRequestBodyData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponse, PostCallWithOptionalParamResponses, PostCallWithoutParametersAndResponseData, PostServiceWithEmptyTagResponse, PostServiceWithEmptyTagResponse2, PutCallWithoutParametersAndResponseData, PutWithFormUrlEncodedData, SchemaWithFormRestrictedKeys, SimpleBoolean, SimpleFile, SimpleFormData, SimpleInteger, SimpleParameter, SimpleReference, SimpleRequestBody, SimpleString, SimpleStringWithPattern, TestErrorCodeData, TestErrorCodeErrors, TestErrorCodeResponses, TypesData, TypesResponse, TypesResponses, UploadFileData, UploadFileResponse, UploadFileResponses, XFooBar } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/msw.gen.ts new file mode 100644 index 0000000000..32b71d0727 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/msw.gen.ts @@ -0,0 +1,208 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { ApiVVersionODataControllerCountResponses, CallWithDuplicateResponsesResponses, CallWithNoContentResponseResponses, CallWithParametersData, CallWithResponseAndNoContentResponseResponses, CallWithResponsesResponses, CallWithResultFromHeaderResponses, CallWithWeirdParameterNamesData, ComplexParamsData, ComplexParamsResponses, ComplexTypesResponses, DeleteFooData3, DummyAResponses, DummyBResponses, FileResponseData, FileResponseResponses, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationResponses, GetCallWithOptionalParamData, ImportData, ImportResponses, MultipartRequestData, MultipartResponseResponses, NonAsciiæøåÆøÅöôêÊ字符串Responses, PostApiVbyApiVersionFormDataData, PostApiVbyApiVersionRequestBodyData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponses, PutWithFormUrlEncodedData, TestErrorCodeResponses, TypesData, TypesResponses, UploadFileData, UploadFileResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type OptionalHttpHandlerFactory = (responseOrResolver?: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +type StringifyPathParams = { + [K in keyof T]-?: string | ReadonlyArray; +}; + +export type SingleHandlerFactories = { + export: OptionalHttpHandlerFactory; + patchApiVbyApiVersionNoTag: OptionalHttpHandlerFactory; + import: HttpHandlerFactory | HttpResponseResolver>; + fooWow: OptionalHttpHandlerFactory; + apiVVersionODataControllerCount: HttpHandlerFactory | HttpResponseResolver>; + getApiVbyApiVersionSimpleOperation: HttpHandlerFactory | HttpResponseResolver>, never>>; + deleteCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + getCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + headCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + optionsCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + patchCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + postCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + putCallWithoutParametersAndResponse: OptionalHttpHandlerFactory; + deleteFoo: OptionalHttpHandlerFactory>, never>>; + callWithDescriptions: OptionalHttpHandlerFactory; + deprecatedCall: OptionalHttpHandlerFactory; + callWithParameters: OptionalHttpHandlerFactory>, CallWithParametersData['body']>>; + callWithWeirdParameterNames: OptionalHttpHandlerFactory>, CallWithWeirdParameterNamesData['body']>>; + getCallWithOptionalParam: OptionalHttpHandlerFactory>; + postCallWithOptionalParam: HttpHandlerFactory | HttpResponseResolver>; + postApiVbyApiVersionRequestBody: OptionalHttpHandlerFactory>; + postApiVbyApiVersionFormData: OptionalHttpHandlerFactory>; + callWithDefaultParameters: OptionalHttpHandlerFactory; + callWithDefaultOptionalParameters: OptionalHttpHandlerFactory; + callToTestOrderOfParams: OptionalHttpHandlerFactory; + duplicateName: OptionalHttpHandlerFactory; + duplicateName2: OptionalHttpHandlerFactory; + duplicateName3: OptionalHttpHandlerFactory; + duplicateName4: OptionalHttpHandlerFactory; + callWithNoContentResponse: OptionalHttpHandlerFactory | HttpResponseResolver>; + callWithResponseAndNoContentResponse: HttpHandlerFactory | HttpResponseResolver>; + dummyA: HttpHandlerFactory | HttpResponseResolver>; + dummyB: OptionalHttpHandlerFactory | HttpResponseResolver>; + callWithResponse: OptionalHttpHandlerFactory; + callWithDuplicateResponses: HttpHandlerFactory | HttpResponseResolver>; + callWithResponses: HttpHandlerFactory | HttpResponseResolver>; + collectionFormat: OptionalHttpHandlerFactory; + types: HttpHandlerFactory | HttpResponseResolver>, never>>; + uploadFile: HttpHandlerFactory | HttpResponseResolver>, UploadFileData['body']>>; + fileResponse: HttpHandlerFactory | HttpResponseResolver>, never>>; + complexTypes: HttpHandlerFactory | HttpResponseResolver>; + multipartResponse: HttpHandlerFactory | HttpResponseResolver>; + multipartRequest: OptionalHttpHandlerFactory>; + complexParams: HttpHandlerFactory | HttpResponseResolver>, ComplexParamsData['body']>>; + callWithResultFromHeader: OptionalHttpHandlerFactory | HttpResponseResolver>; + testErrorCode: OptionalHttpHandlerFactory | HttpResponseResolver>; + nonAsciiæøåÆøÅöôêÊ字符串: HttpHandlerFactory | HttpResponseResolver>; + putWithFormUrlEncoded: OptionalHttpHandlerFactory>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? 'http://localhost:3000/base'; + const of: SingleHandlerFactories = { + export: (res, options) => http.get(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + patchApiVbyApiVersionNoTag: (res, options) => http.patch(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + import: (res, options) => http.post(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + fooWow: (res, options) => http.put(toMswPath('/api/v{api-version}/no+tag', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + apiVVersionODataControllerCount: (res, options) => http.get(toMswPath('/api/v{api-version}/simple/$count', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + getApiVbyApiVersionSimpleOperation: (res, options) => http.get(toMswPath('/api/v{api-version}/simple:operation', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + deleteCallWithoutParametersAndResponse: (res, options) => http.delete(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + getCallWithoutParametersAndResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + headCallWithoutParametersAndResponse: (res, options) => http.head(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + optionsCallWithoutParametersAndResponse: (res, options) => http.options(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + patchCallWithoutParametersAndResponse: (res, options) => http.patch(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + postCallWithoutParametersAndResponse: (res, options) => http.post(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + putCallWithoutParametersAndResponse: (res, options) => http.put(toMswPath('/api/v{api-version}/simple', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + deleteFoo: (res, options) => http.delete(toMswPath('/api/v{api-version}/foo/{foo_param}/bar/{BarParam}', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDescriptions: (res, options) => http.post(toMswPath('/api/v{api-version}/descriptions', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + deprecatedCall: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters/deprecated', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithParameters: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters/{parameterPath}', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithWeirdParameterNames: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + getCallWithOptionalParam: (res, options) => http.get(toMswPath('/api/v{api-version}/parameters', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + postCallWithOptionalParam: (res, options) => http.post(toMswPath('/api/v{api-version}/parameters', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postApiVbyApiVersionRequestBody: (res, options) => http.post(toMswPath('/api/v{api-version}/requestBody', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + postApiVbyApiVersionFormData: (res, options) => http.post(toMswPath('/api/v{api-version}/formData', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDefaultParameters: (res, options) => http.get(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDefaultOptionalParameters: (res, options) => http.post(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callToTestOrderOfParams: (res, options) => http.put(toMswPath('/api/v{api-version}/defaults', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName: (res, options) => http.delete(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName2: (res, options) => http.get(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName3: (res, options) => http.post(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + duplicateName4: (res, options) => http.put(toMswPath('/api/v{api-version}/duplicate', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithNoContentResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/no-content', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponseAndNoContentResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/response-and-no-content', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + dummyA: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/a', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + dummyB: (res, options) => http.get(toMswPath('/api/v{api-version}/multiple-tags/b', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + callWithDuplicateResponses: (res, options) => http.post(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResponses: (res, options) => http.put(toMswPath('/api/v{api-version}/response', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + collectionFormat: (res, options) => http.get(toMswPath('/api/v{api-version}/collectionFormat', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + types: (res, options) => http.get(toMswPath('/api/v{api-version}/types', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + uploadFile: (res, options) => http.post(toMswPath('/api/v{api-version}/upload', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + fileResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/file/{id}', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + complexTypes: (res, options) => http.get(toMswPath('/api/v{api-version}/complex', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + multipartResponse: (res, options) => http.get(toMswPath('/api/v{api-version}/multipart', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + multipartRequest: (res, options) => http.post(toMswPath('/api/v{api-version}/multipart', baseUrl), typeof res === 'function' ? res : resolveToNull, options), + complexParams: (res, options) => http.put(toMswPath('/api/v{api-version}/complex/{id}', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + callWithResultFromHeader: (res, options) => http.post(toMswPath('/api/v{api-version}/header', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + testErrorCode: (res, options) => http.post(toMswPath('/api/v{api-version}/error', baseUrl), typeof res === 'object' && res.status ? () => new HttpResponse(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + nonAsciiæøåÆøÅöôêÊ字符串: (res, options) => http.post(toMswPath('/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + putWithFormUrlEncoded: (res, options) => http.put(toMswPath('/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串', baseUrl), typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + handlers.push(of.export(overrides?.export)); + handlers.push(of.patchApiVbyApiVersionNoTag(overrides?.patchApiVbyApiVersionNoTag)); + addRequiredHandler(of.import, overrides?.import); + handlers.push(of.fooWow(overrides?.fooWow)); + addRequiredHandler(of.apiVVersionODataControllerCount, overrides?.apiVVersionODataControllerCount); + addRequiredHandler(of.getApiVbyApiVersionSimpleOperation, overrides?.getApiVbyApiVersionSimpleOperation); + handlers.push(of.deleteCallWithoutParametersAndResponse(overrides?.deleteCallWithoutParametersAndResponse)); + handlers.push(of.getCallWithoutParametersAndResponse(overrides?.getCallWithoutParametersAndResponse)); + handlers.push(of.headCallWithoutParametersAndResponse(overrides?.headCallWithoutParametersAndResponse)); + handlers.push(of.optionsCallWithoutParametersAndResponse(overrides?.optionsCallWithoutParametersAndResponse)); + handlers.push(of.patchCallWithoutParametersAndResponse(overrides?.patchCallWithoutParametersAndResponse)); + handlers.push(of.postCallWithoutParametersAndResponse(overrides?.postCallWithoutParametersAndResponse)); + handlers.push(of.putCallWithoutParametersAndResponse(overrides?.putCallWithoutParametersAndResponse)); + handlers.push(of.deleteFoo(overrides?.deleteFoo)); + handlers.push(of.callWithDescriptions(overrides?.callWithDescriptions)); + handlers.push(of.deprecatedCall(overrides?.deprecatedCall)); + handlers.push(of.callWithParameters(overrides?.callWithParameters)); + handlers.push(of.callWithWeirdParameterNames(overrides?.callWithWeirdParameterNames)); + handlers.push(of.getCallWithOptionalParam(overrides?.getCallWithOptionalParam)); + addRequiredHandler(of.postCallWithOptionalParam, overrides?.postCallWithOptionalParam); + handlers.push(of.postApiVbyApiVersionRequestBody(overrides?.postApiVbyApiVersionRequestBody)); + handlers.push(of.postApiVbyApiVersionFormData(overrides?.postApiVbyApiVersionFormData)); + handlers.push(of.callWithDefaultParameters(overrides?.callWithDefaultParameters)); + handlers.push(of.callWithDefaultOptionalParameters(overrides?.callWithDefaultOptionalParameters)); + handlers.push(of.callToTestOrderOfParams(overrides?.callToTestOrderOfParams)); + handlers.push(of.duplicateName(overrides?.duplicateName)); + handlers.push(of.duplicateName2(overrides?.duplicateName2)); + handlers.push(of.duplicateName3(overrides?.duplicateName3)); + handlers.push(of.duplicateName4(overrides?.duplicateName4)); + handlers.push(of.callWithNoContentResponse(overrides?.callWithNoContentResponse)); + addRequiredHandler(of.callWithResponseAndNoContentResponse, overrides?.callWithResponseAndNoContentResponse); + addRequiredHandler(of.dummyA, overrides?.dummyA); + handlers.push(of.dummyB(overrides?.dummyB)); + handlers.push(of.callWithResponse(overrides?.callWithResponse)); + addRequiredHandler(of.callWithDuplicateResponses, overrides?.callWithDuplicateResponses); + addRequiredHandler(of.callWithResponses, overrides?.callWithResponses); + handlers.push(of.collectionFormat(overrides?.collectionFormat)); + addRequiredHandler(of.types, overrides?.types); + addRequiredHandler(of.uploadFile, overrides?.uploadFile); + addRequiredHandler(of.fileResponse, overrides?.fileResponse); + addRequiredHandler(of.complexTypes, overrides?.complexTypes); + addRequiredHandler(of.multipartResponse, overrides?.multipartResponse); + handlers.push(of.multipartRequest(overrides?.multipartRequest)); + addRequiredHandler(of.complexParams, overrides?.complexParams); + handlers.push(of.callWithResultFromHeader(overrides?.callWithResultFromHeader)); + handlers.push(of.testErrorCode(overrides?.testErrorCode)); + addRequiredHandler(of.nonAsciiæøåÆøÅöôêÊ字符串, overrides?.nonAsciiæøåÆøÅöôêÊ字符串); + handlers.push(of.putWithFormUrlEncoded(overrides?.putWithFormUrlEncoded)); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/types.gen.ts new file mode 100644 index 0000000000..d819316644 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/default/types.gen.ts @@ -0,0 +1,2100 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: 'http://localhost:3000/base' | (string & {}); +}; + +/** + * Model with number-only name + */ +export type _400 = string; + +/** + * External ref to shared model (A) + */ +export type ExternalRefA = ExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export type ExternalRefB = ExternalSharedModel; + +/** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ +export type CamelCaseCommentWithBreaks = number; + +/** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ +export type CommentWithBreaks = number; + +/** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ +export type CommentWithBackticks = number; + +/** + * Testing backticks and quotes in string: `backticks`, 'quotes', "double quotes" and ```multiple backticks``` should work + */ +export type CommentWithBackticksAndQuotes = number; + +/** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ +export type CommentWithSlashes = number; + +/** + * Testing expression placeholders in string: ${expression} should work + */ +export type CommentWithExpressionPlaceholders = number; + +/** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ +export type CommentWithQuotes = number; + +/** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ +export type CommentWithReservedCharacters = number; + +/** + * This is a simple number + */ +export type SimpleInteger = number; + +/** + * This is a simple boolean + */ +export type SimpleBoolean = boolean; + +/** + * This is a simple string + */ +export type SimpleString = string; + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export type NonAsciiStringæøåÆøÅöôêÊ字符串 = string; + +/** + * This is a simple file + */ +export type SimpleFile = Blob | File; + +/** + * This is a simple reference + */ +export type SimpleReference = ModelWithString; + +/** + * This is a simple string + */ +export type SimpleStringWithPattern = string | null; + +/** + * This is a simple enum with strings + */ +export type EnumWithStrings = 'Success' | 'Warning' | 'Error' | '\'Single Quote\'' | '"Double Quotes"' | 'Non-ascii: øæåôöØÆÅÔÖ字符串'; + +export type EnumWithReplacedCharacters = '\'Single Quote\'' | '"Double Quotes"' | 'øæåôöØÆÅÔÖ字符串' | 3.1 | ''; + +/** + * This is a simple enum with numbers + */ +export type EnumWithNumbers = 1 | 2 | 3 | 1.1 | 1.2 | 1.3 | 100 | 200 | 300 | -100 | -200 | -300 | -1.1 | -1.2 | -1.3; + +/** + * Success=1,Warning=2,Error=3 + */ +export type EnumFromDescription = number; + +/** + * This is a simple enum with numbers + */ +export type EnumWithExtensions = 200 | 400 | 500; + +export type EnumWithXEnumNames = 0 | 1 | 2; + +/** + * This is a simple array with numbers + */ +export type ArrayWithNumbers = Array; + +/** + * This is a simple array with booleans + */ +export type ArrayWithBooleans = Array; + +/** + * This is a simple array with strings + */ +export type ArrayWithStrings = Array; + +/** + * This is a simple array with references + */ +export type ArrayWithReferences = Array; + +/** + * This is a simple array containing an array + */ +export type ArrayWithArray = Array>; + +/** + * This is a simple array with properties + */ +export type ArrayWithProperties = Array<{ + '16x16'?: CamelCaseCommentWithBreaks; + bar?: string; +}>; + +/** + * This is a simple array with any of properties + */ +export type ArrayWithAnyOfProperties = Array<{ + foo?: string; +} | { + bar?: string; +}>; + +export type AnyOfAnyAndNull = { + data?: unknown | null; +}; + +/** + * This is a simple array with any of properties + */ +export type AnyOfArrays = { + results?: Array<{ + foo?: string; + } | { + bar?: string; + }>; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithString = { + [key: string]: string; +}; + +export type DictionaryWithPropertiesAndAdditionalProperties = { + foo?: number; + bar?: boolean; + [key: string]: string | number | boolean | undefined; +}; + +/** + * This is a string reference + */ +export type DictionaryWithReference = { + [key: string]: ModelWithString; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithArray = { + [key: string]: Array; +}; + +/** + * This is a string dictionary + */ +export type DictionaryWithDictionary = { + [key: string]: { + [key: string]: string; + }; +}; + +/** + * This is a complex dictionary + */ +export type DictionaryWithProperties = { + [key: string]: { + foo?: string; + bar?: string; + }; +}; + +/** + * This is a model with one number property + */ +export type ModelWithInteger = { + /** + * This is a simple number property + */ + prop?: number; +}; + +/** + * This is a model with one boolean property + */ +export type ModelWithBoolean = { + /** + * This is a simple boolean property + */ + prop?: boolean; +}; + +/** + * This is a model with one string property + */ +export type ModelWithString = { + /** + * This is a simple string property + */ + prop?: string; +}; + +/** + * This is a model with one string property + */ +export type ModelWithStringError = { + /** + * This is a simple string property + */ + prop?: string; +}; + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export type ModelFromZendesk = string; + +/** + * This is a model with one string property + */ +export type ModelWithNullableString = { + /** + * This is a simple string property + */ + nullableProp1?: string | null; + /** + * This is a simple string property + */ + nullableRequiredProp1: string | null; + /** + * This is a simple string property + */ + nullableProp2?: string | null; + /** + * This is a simple string property + */ + nullableRequiredProp2: string | null; + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; +}; + +/** + * This is a model with one enum + */ +export type ModelWithEnum = { + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; + /** + * These are the HTTP error code enums + */ + statusCode?: '100' | '200 FOO' | '300 FOO_BAR' | '400 foo-bar' | '500 foo.bar' | '600 foo&bar'; + /** + * Simple boolean enum + */ + bool?: true; +}; + +/** + * This is a model with one enum with escaped name + */ +export type ModelWithEnumWithHyphen = { + /** + * Foo-Bar-Baz-Qux + */ + 'foo-bar-baz-qux'?: '3.0'; +}; + +/** + * This is a model with one enum + */ +export type ModelWithEnumFromDescription = { + /** + * Success=1,Warning=2,Error=3 + */ + test?: number; +}; + +/** + * This is a model with nested enums + */ +export type ModelWithNestedEnums = { + dictionaryWithEnum?: { + [key: string]: 'Success' | 'Warning' | 'Error'; + }; + dictionaryWithEnumFromDescription?: { + [key: string]: number; + }; + arrayWithEnum?: Array<'Success' | 'Warning' | 'Error'>; + arrayWithDescription?: Array; + /** + * This is a simple enum with strings + */ + 'foo_bar-enum'?: 'Success' | 'Warning' | 'Error' | 'ØÆÅ字符串'; +}; + +/** + * This is a model with one property containing a reference + */ +export type ModelWithReference = { + prop?: ModelWithProperties; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArrayReadOnlyAndWriteOnly = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArray = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one property containing a dictionary + */ +export type ModelWithDictionary = { + prop?: { + [key: string]: string; + }; +}; + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export type DeprecatedModel = { + /** + * This is a deprecated property + * + * @deprecated + */ + prop?: string; +}; + +/** + * This is a model with one property containing a circular reference + */ +export type ModelWithCircularReference = { + prop?: ModelWithCircularReference; +}; + +/** + * This is a model with one property with a 'one of' relationship + */ +export type CompositionWithOneOf = { + propA?: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; +}; + +/** + * This is a model with one property with a 'one of' relationship where the options are not $ref + */ +export type CompositionWithOneOfAnonymous = { + propA?: { + propA?: string; + } | string | number; +}; + +/** + * Circle + */ +export type ModelCircle = { + kind: string; + radius?: number; +}; + +/** + * Square + */ +export type ModelSquare = { + kind: string; + sideLength?: number; +}; + +/** + * This is a model with one property with a 'one of' relationship where the options are not $ref + */ +export type CompositionWithOneOfDiscriminator = ({ + kind: 'circle'; +} & ModelCircle) | ({ + kind: 'square'; +} & ModelSquare); + +/** + * This is a model with one property with a 'any of' relationship + */ +export type CompositionWithAnyOf = { + propA?: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; +}; + +/** + * This is a model with one property with a 'any of' relationship where the options are not $ref + */ +export type CompositionWithAnyOfAnonymous = { + propA?: { + propA?: string; + } | string | number; +}; + +/** + * This is a model with nested 'any of' property with a type null + */ +export type CompositionWithNestedAnyAndTypeNull = { + propA?: Array | Array; +}; + +export type _3eNum1Период = 'Bird' | 'Dog'; + +export type ConstValue = 'ConstValue'; + +/** + * This is a model with one property with a 'any of' relationship where the options are not $ref + */ +export type CompositionWithNestedAnyOfAndNull = { + /** + * Scopes + */ + propA?: Array<_3eNum1Период | ConstValue> | null; +}; + +/** + * This is a model with one property with a 'one of' relationship + */ +export type CompositionWithOneOfAndNullable = { + propA?: { + boolean?: boolean; + } | ModelWithEnum | ModelWithArray | ModelWithDictionary | null; +}; + +/** + * This is a model that contains a simple dictionary within composition + */ +export type CompositionWithOneOfAndSimpleDictionary = { + propA?: boolean | { + [key: string]: number; + }; +}; + +/** + * This is a model that contains a dictionary of simple arrays within composition + */ +export type CompositionWithOneOfAndSimpleArrayDictionary = { + propA?: boolean | { + [key: string]: Array; + }; +}; + +/** + * This is a model that contains a dictionary of complex arrays (composited) within composition + */ +export type CompositionWithOneOfAndComplexArrayDictionary = { + propA?: boolean | { + [key: string]: Array; + }; +}; + +/** + * This is a model with one property with a 'all of' relationship + */ +export type CompositionWithAllOfAndNullable = { + propA?: ({ + boolean?: boolean; + } & ModelWithEnum & ModelWithArray & ModelWithDictionary) | null; +}; + +/** + * This is a model with one property with a 'any of' relationship + */ +export type CompositionWithAnyOfAndNullable = { + propA?: { + boolean?: boolean; + } | ModelWithEnum | ModelWithArray | ModelWithDictionary | null; +}; + +/** + * This is a base model with two simple optional properties + */ +export type CompositionBaseModel = { + firstName?: string; + lastname?: string; +}; + +/** + * This is a model that extends the base model + */ +export type CompositionExtendedModel = CompositionBaseModel & { + age: number; + firstName: string; + lastname: string; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithProperties = { + required: string; + readonly requiredAndReadOnly: string; + requiredAndNullable: string | null; + string?: string; + number?: number; + boolean?: boolean; + reference?: ModelWithString; + 'property with space'?: string; + default?: string; + try?: string; + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithNestedProperties = { + readonly first: { + readonly second: { + readonly third: string | null; + } | null; + } | null; +}; + +/** + * This is a model with duplicated properties + */ +export type ModelWithDuplicateProperties = { + prop?: ModelWithString; +}; + +/** + * This is a model with ordered properties + */ +export type ModelWithOrderedProperties = { + zebra?: string; + apple?: string; + hawaii?: string; +}; + +/** + * This is a model with duplicated imports + */ +export type ModelWithDuplicateImports = { + propA?: ModelWithString; + propB?: ModelWithString; + propC?: ModelWithString; +}; + +/** + * This is a model that extends another model + */ +export type ModelThatExtends = ModelWithString & { + propExtendsA?: string; + propExtendsB?: ModelWithString; +}; + +/** + * This is a model that extends another model + */ +export type ModelThatExtendsExtends = ModelWithString & ModelThatExtends & { + propExtendsC?: string; + propExtendsD?: ModelWithString; +}; + +/** + * This is a model that contains a some patterns + */ +export type ModelWithPattern = { + key: string; + name: string; + readonly enabled?: boolean; + readonly modified?: string; + id?: string; + text?: string; + patternWithSingleQuotes?: string; + patternWithNewline?: string; + patternWithBacktick?: string; + patternWithUnicode?: string; +}; + +export type File = { + /** + * Id + */ + readonly id?: string; + /** + * Updated at + */ + readonly updated_at?: string; + /** + * Created at + */ + readonly created_at?: string; + /** + * Mime + */ + mime: string; + /** + * File + */ + readonly file?: string; +}; + +export type Default = { + name?: string; +}; + +export type Pageable = { + page?: number; + size?: number; + sort?: Array; +}; + +/** + * This is a free-form object without additionalProperties. + */ +export type FreeFormObjectWithoutAdditionalProperties = { + [key: string]: unknown; +}; + +/** + * This is a free-form object with additionalProperties: true. + */ +export type FreeFormObjectWithAdditionalPropertiesEqTrue = { + [key: string]: unknown; +}; + +/** + * This is a free-form object with additionalProperties: {}. + */ +export type FreeFormObjectWithAdditionalPropertiesEqEmptyObject = { + [key: string]: unknown; +}; + +export type ModelWithConst = { + String?: 'String'; + number?: 0; + null?: null; + withType?: 'Some string'; +}; + +/** + * This is a model with one property and additionalProperties: true + */ +export type ModelWithAdditionalPropertiesEqTrue = { + /** + * This is a simple string property + */ + prop?: string; + [key: string]: unknown; +}; + +export type NestedAnyOfArraysNullable = { + nullableArray?: Array | null; +}; + +export type CompositionWithOneOfAndProperties = ({ + foo: SimpleParameter; +} | { + bar: NonAsciiStringæøåÆøÅöôêÊ字符串; +}) & { + baz: number | null; + qux: number; +}; + +/** + * An object that can be null + */ +export type NullableObject = { + foo?: string; +} | null; + +/** + * Some % character + */ +export type CharactersInDescription = string; + +export type ModelWithNullableObject = { + data?: NullableObject; +}; + +/** + * An object with additional properties that can be null (anyOf ref + null) + */ +export type ModelWithAdditionalPropertiesRef = { + [key: string]: NullableObject | null; +}; + +export type ModelWithOneOfEnum = { + foo: 'Bar'; +} | { + foo: 'Baz'; +} | { + foo: 'Qux'; +} | { + content: string; + foo: 'Quux'; +} | { + content: [ + string, + string + ]; + foo: 'Corge'; +}; + +export type ModelWithNestedArrayEnumsDataFoo = 'foo' | 'bar'; + +export type ModelWithNestedArrayEnumsDataBar = 'baz' | 'qux'; + +export type ModelWithNestedArrayEnumsData = { + foo?: Array; + bar?: Array; +}; + +export type ModelWithNestedArrayEnums = { + array_strings?: Array; + data?: ModelWithNestedArrayEnumsData; +}; + +export type ModelWithNestedCompositionEnums = { + foo?: ModelWithNestedArrayEnumsDataFoo; +}; + +export type ModelWithReadOnlyAndWriteOnly = { + foo: string; + readonly bar: string; +}; + +export type ModelWithConstantSizeArray = [ + number, + number +]; + +export type ModelWithAnyOfConstantSizeArray = [ + number | string, + number | string, + number | string +]; + +export type ModelWithPrefixItemsConstantSizeArray = [ + ModelWithInteger, + number | string, + string +]; + +export type ModelWithAnyOfConstantSizeArrayNullable = [ + number | null | string, + number | null | string, + number | null | string +]; + +export type ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = [ + number | Import, + number | Import +]; + +export type ModelWithAnyOfConstantSizeArrayAndIntersect = [ + number & string, + number & string +]; + +export type ModelWithNumericEnumUnion = { + /** + * Период + */ + value?: -10 | -1 | 0 | 1 | 3 | 6 | 12; +}; + +/** + * Some description with `back ticks` + */ +export type ModelWithBackticksInDescription = { + /** + * The template `that` should be used for parsing and importing the contents of the CSV file. + * + *

There is one placeholder currently supported:

  • ${x} - refers to the n-th column in the CSV file, e.g. ${1}, ${2}, ...)

Example of a correct JSON template:

+ *
+     * [
+     * {
+     * "resourceType": "Asset",
+     * "identifier": {
+     * "name": "${1}",
+     * "domain": {
+     * "name": "${2}",
+     * "community": {
+     * "name": "Some Community"
+     * }
+     * }
+     * },
+     * "attributes" : {
+     * "00000000-0000-0000-0000-000000003115" : [ {
+     * "value" : "${3}"
+     * } ],
+     * "00000000-0000-0000-0000-000000000222" : [ {
+     * "value" : "${4}"
+     * } ]
+     * }
+     * }
+     * ]
+     * 
+ */ + template?: string; +}; + +export type ModelWithOneOfAndProperties = (SimpleParameter | NonAsciiStringæøåÆøÅöôêÊ字符串) & { + baz: number | null; + qux: number; +}; + +/** + * Model used to test deduplication strategy (unused) + */ +export type ParameterSimpleParameterUnused = string; + +/** + * Model used to test deduplication strategy + */ +export type PostServiceWithEmptyTagResponse = string; + +/** + * Model used to test deduplication strategy + */ +export type PostServiceWithEmptyTagResponse2 = string; + +/** + * Model used to test deduplication strategy + */ +export type DeleteFooData = string; + +/** + * Model used to test deduplication strategy + */ +export type DeleteFooData2 = string; + +/** + * Model with restricted keyword name + */ +export type Import = string; + +export type SchemaWithFormRestrictedKeys = { + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + object?: { + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + }; + array?: Array<{ + description?: string; + 'x-enum-descriptions'?: string; + 'x-enum-varnames'?: string; + 'x-enumNames'?: string; + title?: string; + }>; +}; + +/** + * This schema was giving PascalCase transformations a hard time + */ +export type IoK8sApimachineryPkgApisMetaV1DeleteOptions = { + /** + * Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned. + */ + preconditions?: IoK8sApimachineryPkgApisMetaV1Preconditions; +}; + +/** + * This schema was giving PascalCase transformations a hard time + */ +export type IoK8sApimachineryPkgApisMetaV1Preconditions = { + /** + * Specifies the target ResourceVersion + */ + resourceVersion?: string; + /** + * Specifies the target UID. + */ + uid?: string; +}; + +export type AdditionalPropertiesUnknownIssue = { + [key: string]: string | number; +}; + +export type AdditionalPropertiesUnknownIssue2 = { + [key: string]: string | number; +}; + +export type AdditionalPropertiesUnknownIssue3 = string & { + entries: { + [key: string]: AdditionalPropertiesUnknownIssue; + }; +}; + +export type AdditionalPropertiesIntegerIssue = { + value: number; + [key: string]: number; +}; + +export type OneOfAllOfIssue = ((ConstValue | GenericSchemaDuplicateIssue1SystemBoolean) & _3eNum1Период) | GenericSchemaDuplicateIssue1SystemString; + +export type GenericSchemaDuplicateIssue1SystemBoolean = { + item?: boolean; + error?: string | null; + readonly hasError?: boolean; + data?: { + [key: string]: never; + }; +}; + +export type GenericSchemaDuplicateIssue1SystemString = { + item?: string | null; + error?: string | null; + readonly hasError?: boolean; +}; + +export type ExternalSharedModel = { + id: string; + name?: string; +}; + +/** + * This is a model with one property containing a reference + */ +export type ModelWithReferenceWritable = { + prop?: ModelWithPropertiesWritable; +}; + +/** + * This is a model with one property containing an array + */ +export type ModelWithArrayReadOnlyAndWriteOnlyWritable = { + prop?: Array; + propWithFile?: Array; + propWithNumber?: Array; +}; + +/** + * This is a model with one nested property + */ +export type ModelWithPropertiesWritable = { + required: string; + requiredAndNullable: string | null; + string?: string; + number?: number; + boolean?: boolean; + reference?: ModelWithString; + 'property with space'?: string; + default?: string; + try?: string; +}; + +/** + * This is a model that contains a some patterns + */ +export type ModelWithPatternWritable = { + key: string; + name: string; + id?: string; + text?: string; + patternWithSingleQuotes?: string; + patternWithNewline?: string; + patternWithBacktick?: string; + patternWithUnicode?: string; +}; + +export type FileWritable = { + /** + * Mime + */ + mime: string; +}; + +export type ModelWithReadOnlyAndWriteOnlyWritable = { + foo: string; + baz: string; +}; + +export type ModelWithAnyOfConstantSizeArrayWithNSizeAndOptionsWritable = [ + number | Import, + number | Import +]; + +export type AdditionalPropertiesUnknownIssueWritable = { + [key: string]: string | number; +}; + +export type OneOfAllOfIssueWritable = ((ConstValue | GenericSchemaDuplicateIssue1SystemBoolean) & _3eNum1Период) | GenericSchemaDuplicateIssue1SystemString; + +export type GenericSchemaDuplicateIssue1SystemBooleanWritable = { + item?: boolean; + error?: string | null; + data?: { + [key: string]: never; + }; +}; + +export type GenericSchemaDuplicateIssue1SystemStringWritable = { + item?: string | null; + error?: string | null; +}; + +/** + * This is a reusable parameter + */ +export type SimpleParameter = string; + +/** + * Parameter with illegal characters + */ +export type XFooBar = ModelWithString; + +/** + * A reusable request body + */ +export type SimpleRequestBody = ModelWithString; + +/** + * A reusable request body + */ +export type SimpleFormData = ModelWithString; + +export type ExportData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type PatchApiVbyApiVersionNoTagData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type PatchApiVbyApiVersionNoTagResponses = { + /** + * OK + */ + default: unknown; +}; + +export type ImportData = { + body: ModelWithReadOnlyAndWriteOnlyWritable | ModelWithArrayReadOnlyAndWriteOnlyWritable; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type ImportResponses = { + /** + * Success + */ + 200: ModelFromZendesk; + /** + * Default success response + */ + default: ModelWithReadOnlyAndWriteOnly; +}; + +export type ImportResponse = ImportResponses[keyof ImportResponses]; + +export type FooWowData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no+tag'; +}; + +export type FooWowResponses = { + /** + * OK + */ + default: unknown; +}; + +export type ApiVVersionODataControllerCountData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple/$count'; +}; + +export type ApiVVersionODataControllerCountResponses = { + /** + * Success + */ + 200: ModelFromZendesk; +}; + +export type ApiVVersionODataControllerCountResponse = ApiVVersionODataControllerCountResponses[keyof ApiVVersionODataControllerCountResponses]; + +export type GetApiVbyApiVersionSimpleOperationData = { + body?: never; + path: { + /** + * foo in method + */ + foo_param: string; + }; + query?: never; + url: '/api/v{api-version}/simple:operation'; +}; + +export type GetApiVbyApiVersionSimpleOperationErrors = { + /** + * Default error response + */ + default: ModelWithBoolean; +}; + +export type GetApiVbyApiVersionSimpleOperationError = GetApiVbyApiVersionSimpleOperationErrors[keyof GetApiVbyApiVersionSimpleOperationErrors]; + +export type GetApiVbyApiVersionSimpleOperationResponses = { + /** + * Response is a simple number + */ + 200: number; +}; + +export type GetApiVbyApiVersionSimpleOperationResponse = GetApiVbyApiVersionSimpleOperationResponses[keyof GetApiVbyApiVersionSimpleOperationResponses]; + +export type DeleteCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type GetCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type HeadCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type OptionsCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PatchCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PostCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type PutCallWithoutParametersAndResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/simple'; +}; + +export type DeleteFooData3 = { + body?: never; + headers: { + /** + * Parameter with illegal characters + */ + 'x-Foo-Bar': ModelWithString; + }; + path: { + /** + * foo in method + */ + foo_param: string; + /** + * bar in method + */ + BarParam: string; + }; + query?: never; + url: '/api/v{api-version}/foo/{foo_param}/bar/{BarParam}'; +}; + +export type CallWithDescriptionsData = { + body?: never; + path?: never; + query?: { + /** + * Testing multiline comments in string: First line + * Second line + * + * Fourth line + */ + parameterWithBreaks?: string; + /** + * Testing backticks in string: `backticks` and ```multiple backticks``` should work + */ + parameterWithBackticks?: string; + /** + * Testing slashes in string: \backwards\\\ and /forwards/// should work + */ + parameterWithSlashes?: string; + /** + * Testing expression placeholders in string: ${expression} should work + */ + parameterWithExpressionPlaceholders?: string; + /** + * Testing quotes in string: 'single quote''' and "double quotes""" should work + */ + parameterWithQuotes?: string; + /** + * Testing reserved characters in string: * inline * and ** inline ** should work + */ + parameterWithReservedCharacters?: string; + }; + url: '/api/v{api-version}/descriptions'; +}; + +export type DeprecatedCallData = { + body?: never; + headers: { + /** + * This parameter is deprecated + * + * @deprecated + */ + parameter: DeprecatedModel | null; + }; + path?: never; + query?: never; + url: '/api/v{api-version}/parameters/deprecated'; +}; + +export type CallWithParametersData = { + /** + * This is the parameter that goes into the body + */ + body: { + [key: string]: unknown; + } | null; + headers: { + /** + * This is the parameter that goes into the header + */ + parameterHeader: string | null; + }; + path: { + /** + * This is the parameter that goes into the path + */ + parameterPath: string | null; + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query: { + foo_ref_enum?: ModelWithNestedArrayEnumsDataFoo; + foo_all_of_enum: ModelWithNestedArrayEnumsDataFoo; + /** + * This is the parameter that goes into the query params + */ + cursor: string | null; + }; + url: '/api/v{api-version}/parameters/{parameterPath}'; +}; + +export type CallWithWeirdParameterNamesData = { + /** + * This is the parameter that goes into the body + */ + body: ModelWithString | null; + headers: { + /** + * This is the parameter that goes into the request header + */ + 'parameter.header': string | null; + }; + path: { + /** + * This is the parameter that goes into the path + */ + 'parameter.path.1'?: string; + /** + * This is the parameter that goes into the path + */ + 'parameter-path-2'?: string; + /** + * This is the parameter that goes into the path + */ + 'PARAMETER-PATH-3'?: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query: { + /** + * This is the parameter with a reserved keyword + */ + default?: string; + /** + * This is the parameter that goes into the request query params + */ + 'parameter-query': string | null; + }; + url: '/api/v{api-version}/parameters/{parameter.path.1}/{parameter-path-2}/{PARAMETER-PATH-3}'; +}; + +export type GetCallWithOptionalParamData = { + /** + * This is a required parameter + */ + body: ModelWithOneOfEnum; + path?: never; + query?: { + /** + * This is an optional parameter + */ + page?: number; + }; + url: '/api/v{api-version}/parameters'; +}; + +export type PostCallWithOptionalParamData = { + /** + * This is an optional parameter + */ + body?: { + offset?: number | null; + }; + path?: never; + query: { + /** + * This is a required parameter + */ + parameter: Pageable; + }; + url: '/api/v{api-version}/parameters'; +}; + +export type PostCallWithOptionalParamResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; +}; + +export type PostCallWithOptionalParamResponse = PostCallWithOptionalParamResponses[keyof PostCallWithOptionalParamResponses]; + +export type PostApiVbyApiVersionRequestBodyData = { + /** + * A reusable request body + */ + body?: SimpleRequestBody; + path?: never; + query?: { + /** + * This is a reusable parameter + */ + parameter?: string; + }; + url: '/api/v{api-version}/requestBody'; +}; + +export type PostApiVbyApiVersionFormDataData = { + /** + * A reusable request body + */ + body?: SimpleFormData; + path?: never; + query?: { + /** + * This is a reusable parameter + */ + parameter?: string; + }; + url: '/api/v{api-version}/formData'; +}; + +export type CallWithDefaultParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string with default value + */ + parameterString?: string | null; + /** + * This is a simple number with default value + */ + parameterNumber?: number | null; + /** + * This is a simple boolean with default value + */ + parameterBoolean?: boolean | null; + /** + * This is a simple enum with default value + */ + parameterEnum?: 'Success' | 'Warning' | 'Error'; + /** + * This is a simple model with default value + */ + parameterModel?: ModelWithString | null; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallWithDefaultOptionalParametersData = { + body?: never; + path?: never; + query?: { + /** + * This is a simple string that is optional with default value + */ + parameterString?: string; + /** + * This is a simple number that is optional with default value + */ + parameterNumber?: number; + /** + * This is a simple boolean that is optional with default value + */ + parameterBoolean?: boolean; + /** + * This is a simple enum that is optional with default value + */ + parameterEnum?: 'Success' | 'Warning' | 'Error'; + /** + * This is a simple model that is optional with default value + */ + parameterModel?: ModelWithString; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type CallToTestOrderOfParamsData = { + body?: never; + path?: never; + query: { + /** + * This is a optional string with default + */ + parameterOptionalStringWithDefault?: string; + /** + * This is a optional string with empty default + */ + parameterOptionalStringWithEmptyDefault?: string; + /** + * This is a optional string with no default + */ + parameterOptionalStringWithNoDefault?: string; + /** + * This is a string with default + */ + parameterStringWithDefault: string; + /** + * This is a string with empty default + */ + parameterStringWithEmptyDefault: string; + /** + * This is a string with no default + */ + parameterStringWithNoDefault: string; + /** + * This is a string that can be null with no default + */ + parameterStringNullableWithNoDefault?: string | null; + /** + * This is a string that can be null with default + */ + parameterStringNullableWithDefault?: string | null; + }; + url: '/api/v{api-version}/defaults'; +}; + +export type DuplicateNameData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName2Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName3Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type DuplicateName4Data = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/duplicate'; +}; + +export type CallWithNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/no-content'; +}; + +export type CallWithNoContentResponseResponses = { + /** + * Success + */ + 204: void; +}; + +export type CallWithNoContentResponseResponse = CallWithNoContentResponseResponses[keyof CallWithNoContentResponseResponses]; + +export type CallWithResponseAndNoContentResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/response-and-no-content'; +}; + +export type CallWithResponseAndNoContentResponseResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Success + */ + 204: void; +}; + +export type CallWithResponseAndNoContentResponseResponse = CallWithResponseAndNoContentResponseResponses[keyof CallWithResponseAndNoContentResponseResponses]; + +export type DummyAData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/a'; +}; + +export type DummyAResponses = { + 200: _400; +}; + +export type DummyAResponse = DummyAResponses[keyof DummyAResponses]; + +export type DummyBData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multiple-tags/b'; +}; + +export type DummyBResponses = { + /** + * Success + */ + 204: void; +}; + +export type DummyBResponse = DummyBResponses[keyof DummyBResponses]; + +export type CallWithResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponseResponses = { + default: Import; +}; + +export type CallWithResponseResponse = CallWithResponseResponses[keyof CallWithResponseResponses]; + +export type CallWithDuplicateResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithDuplicateResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for 4XX errors + */ + '4XX': DictionaryWithArray; + /** + * Default error response + */ + default: ModelWithBoolean; +}; + +export type CallWithDuplicateResponsesError = CallWithDuplicateResponsesErrors[keyof CallWithDuplicateResponsesErrors]; + +export type CallWithDuplicateResponsesResponses = { + /** + * Message for 200 response + */ + 200: ModelWithBoolean & ModelWithInteger; + /** + * Message for 201 response + */ + 201: ModelWithString; + /** + * Message for 202 response + */ + 202: ModelWithString; +}; + +export type CallWithDuplicateResponsesResponse = CallWithDuplicateResponsesResponses[keyof CallWithDuplicateResponsesResponses]; + +export type CallWithResponsesData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/response'; +}; + +export type CallWithResponsesErrors = { + /** + * Message for 500 error + */ + 500: ModelWithStringError; + /** + * Message for 501 error + */ + 501: ModelWithStringError; + /** + * Message for 502 error + */ + 502: ModelWithStringError; + /** + * Message for default response + */ + default: ModelWithStringError; +}; + +export type CallWithResponsesError = CallWithResponsesErrors[keyof CallWithResponsesErrors]; + +export type CallWithResponsesResponses = { + /** + * Message for 200 response + */ + 200: { + readonly '@namespace.string'?: string; + readonly '@namespace.integer'?: number; + readonly value?: Array; + }; + /** + * Message for 201 response + */ + 201: ModelThatExtends; + /** + * Message for 202 response + */ + 202: ModelThatExtendsExtends; +}; + +export type CallWithResponsesResponse = CallWithResponsesResponses[keyof CallWithResponsesResponses]; + +export type CollectionFormatData = { + body?: never; + path?: never; + query: { + /** + * This is an array parameter that is sent as csv format (comma-separated values) + */ + parameterArrayCSV: Array | null; + /** + * This is an array parameter that is sent as ssv format (space-separated values) + */ + parameterArraySSV: Array | null; + /** + * This is an array parameter that is sent as tsv format (tab-separated values) + */ + parameterArrayTSV: Array | null; + /** + * This is an array parameter that is sent as pipes format (pipe-separated values) + */ + parameterArrayPipes: Array | null; + /** + * This is an array parameter that is sent as multi format (multiple parameter instances) + */ + parameterArrayMulti: Array | null; + }; + url: '/api/v{api-version}/collectionFormat'; +}; + +export type TypesData = { + body?: never; + path?: { + /** + * This is a number parameter + */ + id?: number; + }; + query: { + /** + * This is a number parameter + */ + parameterNumber: number; + /** + * This is a string parameter + */ + parameterString: string | null; + /** + * This is a boolean parameter + */ + parameterBoolean: boolean | null; + /** + * This is an object parameter + */ + parameterObject: { + [key: string]: unknown; + } | null; + /** + * This is an array parameter + */ + parameterArray: Array | null; + /** + * This is a dictionary parameter + */ + parameterDictionary: { + [key: string]: unknown; + } | null; + /** + * This is an enum parameter + */ + parameterEnum: 'Success' | 'Warning' | 'Error' | null; + }; + url: '/api/v{api-version}/types'; +}; + +export type TypesResponses = { + /** + * Response is a simple number + */ + 200: number; + /** + * Response is a simple string + */ + 201: string; + /** + * Response is a simple boolean + */ + 202: boolean; + /** + * Response is a simple object + */ + 203: { + [key: string]: unknown; + }; +}; + +export type TypesResponse = TypesResponses[keyof TypesResponses]; + +export type UploadFileData = { + body: Blob | File; + path: { + /** + * api-version should be required in standalone clients + */ + 'api-version': string | null; + }; + query?: never; + url: '/api/v{api-version}/upload'; +}; + +export type UploadFileResponses = { + 200: boolean; +}; + +export type UploadFileResponse = UploadFileResponses[keyof UploadFileResponses]; + +export type FileResponseData = { + body?: never; + path: { + id: string; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query?: never; + url: '/api/v{api-version}/file/{id}'; +}; + +export type FileResponseResponses = { + /** + * Success + */ + 200: Blob | File; +}; + +export type FileResponseResponse = FileResponseResponses[keyof FileResponseResponses]; + +export type ComplexTypesData = { + body?: never; + path?: never; + query: { + /** + * Parameter containing object + */ + parameterObject: { + first?: { + second?: { + third?: string; + }; + }; + }; + /** + * Parameter containing reference + */ + parameterReference: ModelWithString; + }; + url: '/api/v{api-version}/complex'; +}; + +export type ComplexTypesErrors = { + /** + * 400 `server` error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type ComplexTypesResponses = { + /** + * Successful response + */ + 200: Array; +}; + +export type ComplexTypesResponse = ComplexTypesResponses[keyof ComplexTypesResponses]; + +export type MultipartResponseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/multipart'; +}; + +export type MultipartResponseResponses = { + /** + * OK + */ + 200: { + file?: Blob | File; + metadata?: { + foo?: string; + bar?: string; + }; + }; +}; + +export type MultipartResponseResponse = MultipartResponseResponses[keyof MultipartResponseResponses]; + +export type MultipartRequestData = { + body?: { + content?: Blob | File; + data?: ModelWithString | null; + }; + path?: never; + query?: never; + url: '/api/v{api-version}/multipart'; +}; + +export type ComplexParamsData = { + body?: { + readonly key: string | null; + name: string | null; + enabled?: boolean; + type: 'Monkey' | 'Horse' | 'Bird'; + listOfModels?: Array | null; + listOfStrings?: Array | null; + parameters: ModelWithString | ModelWithEnum | ModelWithArray | ModelWithDictionary; + readonly user?: { + readonly id?: number; + readonly name?: string | null; + }; + }; + path: { + id: number; + /** + * api-version should be required in standalone clients + */ + 'api-version': string; + }; + query?: never; + url: '/api/v{api-version}/complex/{id}'; +}; + +export type ComplexParamsResponses = { + /** + * Success + */ + 200: ModelWithString; +}; + +export type ComplexParamsResponse = ComplexParamsResponses[keyof ComplexParamsResponses]; + +export type CallWithResultFromHeaderData = { + body?: never; + path?: never; + query?: never; + url: '/api/v{api-version}/header'; +}; + +export type CallWithResultFromHeaderErrors = { + /** + * 400 server error + */ + 400: unknown; + /** + * 500 server error + */ + 500: unknown; +}; + +export type CallWithResultFromHeaderResponses = { + /** + * Successful response + */ + 200: unknown; +}; + +export type TestErrorCodeData = { + body?: never; + path?: never; + query: { + /** + * Status code to return + */ + status: number; + }; + url: '/api/v{api-version}/error'; +}; + +export type TestErrorCodeErrors = { + /** + * Custom message: Internal Server Error + */ + 500: unknown; + /** + * Custom message: Not Implemented + */ + 501: unknown; + /** + * Custom message: Bad Gateway + */ + 502: unknown; + /** + * Custom message: Service Unavailable + */ + 503: unknown; +}; + +export type TestErrorCodeResponses = { + /** + * Custom message: Successful response + */ + 200: unknown; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Data = { + body?: never; + path?: never; + query: { + /** + * Dummy input param + */ + nonAsciiParamæøåÆØÅöôêÊ: number; + }; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Responses = { + /** + * Successful response + */ + 200: Array; +}; + +export type NonAsciiæøåÆøÅöôêÊ字符串Response = NonAsciiæøåÆøÅöôêÊ字符串Responses[keyof NonAsciiæøåÆøÅöôêÊ字符串Responses]; + +export type PutWithFormUrlEncodedData = { + body: ArrayWithStrings; + path?: never; + query?: never; + url: '/api/v{api-version}/non-ascii-æøåÆØÅöôêÊ字符串'; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/index.ts new file mode 100644 index 0000000000..b545604fba --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses, Person, PostFooData, PostFooResponse, PostFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/msw.gen.ts new file mode 100644 index 0000000000..096bd2386d --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/msw.gen.ts @@ -0,0 +1,64 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses, PostFooData, PostFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: HttpHandlerFactory | HttpResponseResolver>; + postFoo: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? ''; + const of: SingleHandlerFactories = { + getFoo: (res, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postFoo: (res, options) => http.post(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + addRequiredHandler(of.getFoo, overrides?.getFoo); + addRequiredHandler(of.postFoo, overrides?.postFoo); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/types.gen.ts new file mode 100644 index 0000000000..70782e584a --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example-disabled/types.gen.ts @@ -0,0 +1,50 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; + +export type Person = { + firstName?: string; + lastName?: unknown; + age?: number; +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: Person; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; + +export type PostFooData = { + body: string; + path?: never; + query?: never; + url: '/foo'; +}; + +export type PostFooResponses = { + /** + * OK + */ + 200: { + fullName?: string; + age?: number; + }; + /** + * SUCCESSFUL + */ + 204: void; +}; + +export type PostFooResponse = PostFooResponses[keyof PostFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/index.ts new file mode 100644 index 0000000000..b545604fba --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses, Person, PostFooData, PostFooResponse, PostFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/msw.gen.ts new file mode 100644 index 0000000000..e7eea1763e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/msw.gen.ts @@ -0,0 +1,57 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses, PostFooData, PostFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type OptionalHttpHandlerFactory = (responseOrResolver?: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: OptionalHttpHandlerFactory | HttpResponseResolver>; + postFoo: OptionalHttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? ''; + const of: SingleHandlerFactories = { + getFoo: (res = { result: { + firstName: 'Marry', + lastName: 'Jane', + age: 30 + }, status: 200 }, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options), + postFoo: (res = { result: { fullName: 'John Doe', age: 34 }, status: 200 }, options) => http.post(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const overrides = options?.overrides; + const handlers: Array = []; + handlers.push(of.getFoo(overrides?.getFoo)); + handlers.push(of.postFoo(overrides?.postFoo)); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/types.gen.ts new file mode 100644 index 0000000000..70782e584a --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-example/types.gen.ts @@ -0,0 +1,50 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; + +export type Person = { + firstName?: string; + lastName?: unknown; + age?: number; +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: Person; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; + +export type PostFooData = { + body: string; + path?: never; + query?: never; + url: '/foo'; +}; + +export type PostFooResponses = { + /** + * OK + */ + 200: { + fullName?: string; + age?: number; + }; + /** + * SUCCESSFUL + */ + 204: void; +}; + +export type PostFooResponse = PostFooResponses[keyof PostFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/index.ts new file mode 100644 index 0000000000..f389f85547 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/msw.gen.ts new file mode 100644 index 0000000000..666c271a32 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/msw.gen.ts @@ -0,0 +1,50 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type OptionalHttpHandlerFactory = (responseOrResolver?: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: OptionalHttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? ''; + const of: SingleHandlerFactories = { + getFoo: (res = { result: { name: 'Alice' }, status: 200 }, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const overrides = options?.overrides; + const handlers: Array = []; + handlers.push(of.getFoo(overrides?.getFoo)); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/types.gen.ts new file mode 100644 index 0000000000..a0b37c70a4 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/response-types/types.gen.ts @@ -0,0 +1,23 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: { + name?: string; + }; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/index.ts new file mode 100644 index 0000000000..f389f85547 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { ClientOptions, GetFooData, GetFooResponse, GetFooResponses } from './types.gen'; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/msw.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/msw.gen.ts new file mode 100644 index 0000000000..59e8e0c5fb --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/msw.gen.ts @@ -0,0 +1,61 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { http, type HttpHandler, HttpResponse, type HttpResponseResolver, type RequestHandlerOptions } from 'msw'; + +import type { GetFooResponses } from './types.gen'; + +const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}`; + +const resolveToNull = () => new HttpResponse(null); + +type ToResponseUnion = { + [K in Extract]: { + status: K; + result: T[K]; + }; +}[Extract]; + +type HttpHandlerFactory = (responseOrResolver: ResponseOrResolver, options?: RequestHandlerOptions) => HttpHandler; + +export type SingleHandlerFactories = { + getFoo: HttpHandlerFactory | HttpResponseResolver>; +}; + +export type OfAllOptions = { + onMissingMock?: 'error' | 'skip'; + overrides?: { + [K in keyof SingleHandlerFactories]?: Parameters[0]; + }; +}; + +export type MswHandlerFactory = { + of: SingleHandlerFactories; + ofAll: (options?: OfAllOptions) => Array; +}; + +export const createMswHandlerFactory = (config?: { + baseUrl?: string; +}): MswHandlerFactory => { + const baseUrl = config?.baseUrl ?? 'https://foo.com/v1'; + const of: SingleHandlerFactories = { + getFoo: (res, options) => http.get(toMswPath('/foo', baseUrl), typeof res === 'object' && res.status ? () => HttpResponse.json(res.result ?? null, { status: res.status }) : typeof res === 'function' ? res : resolveToNull, options) + }; + const ofAll = (options?: OfAllOptions): Array => { + const onMissingMock = options?.onMissingMock ?? 'skip'; + const overrides = options?.overrides; + const handlers: Array = []; + const addRequiredHandler = (handler: (value: Value | (() => HttpResponse)) => HttpHandler, override: Value | undefined) => { + if (override != null) { + handlers.push(handler(override)); + } + else { + if (onMissingMock === 'error') { + handlers.push(handler(() => new HttpResponse('[heyapi-msw] The mock of this request is not implemented.', { status: 501 }))); + } + } + }; + addRequiredHandler(of.getFoo, overrides?.getFoo); + return handlers; + }; + return { of, ofAll }; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/types.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/types.gen.ts new file mode 100644 index 0000000000..d7a0ac4610 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/msw/servers/types.gen.ts @@ -0,0 +1,21 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: 'https://foo.com/v1' | `${string}://${string}/v1` | (string & {}); +}; + +export type GetFooData = { + body?: never; + path?: never; + query?: never; + url: '/foo'; +}; + +export type GetFooResponses = { + /** + * OK + */ + 200: string; +}; + +export type GetFooResponse = GetFooResponses[keyof GetFooResponses]; diff --git a/packages/openapi-ts-tests/main/test/plugins.test.ts b/packages/openapi-ts-tests/main/test/plugins.test.ts index 426e15a52e..4eab34e3bd 100644 --- a/packages/openapi-ts-tests/main/test/plugins.test.ts +++ b/packages/openapi-ts-tests/main/test/plugins.test.ts @@ -9,7 +9,7 @@ import { getFilePaths, getSpecsPath } from '../../utils'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const versions = ['2.0.x', '3.0.x', '3.1.x']; +const versions = ['2.0.x', '3.0.x', '3.1.x'] as const; for (const version of versions) { const namespace = 'plugins'; @@ -468,6 +468,47 @@ for (const version of versions) { }), description: 'generate Fastify types with Fastify plugin', }, + { + config: createConfig({ + output: 'default', + plugins: ['msw'], + }), + description: 'generate MSW mock handlers with MSW plugin', + }, + { + config: createConfig({ + input: 'response-example.yaml', + output: 'response-example', + plugins: ['msw'], + }), + description: 'generate MSW mock handlers from spec with example with MSW plugin', + }, + { + config: createConfig({ + input: 'response-example.yaml', + output: 'response-example-disabled', + plugins: [{ name: 'msw', valueSources: [] }], + }), + description: + 'generate MSW mock handlers from spec with example with MSW plugin but disabled', + }, + version !== '2.0.x' && { + config: createConfig({ + input: 'response-types.yaml', + output: 'response-types', + plugins: ['msw'], + }), + description: + 'generate MSW mock handlers from spec with multiple response types with MSW plugin', + }, + { + config: createConfig({ + input: 'servers.yaml', + output: 'servers', + plugins: ['msw'], + }), + description: 'generate MSW mock handlers from spec with servers field', + }, { config: createConfig({ input: 'transforms-read-write.yaml', @@ -580,7 +621,7 @@ for (const version of versions) { }), description: 'generate Angular requests and resources (class)', }, - ]; + ].filter((val) => typeof val === 'object'); it.each(scenarios)('$description', async ({ config }) => { await createClient(config); diff --git a/packages/openapi-ts/package.json b/packages/openapi-ts/package.json index 65f0f66d24..b7797e0459 100644 --- a/packages/openapi-ts/package.json +++ b/packages/openapi-ts/package.json @@ -89,6 +89,7 @@ "axios": "1.13.4", "eslint": "9.39.1", "ky": "1.14.3", + "msw": "2.10.2", "nuxt": "3.14.1592", "ofetch": "1.5.1", "rxjs": "7.8.2", @@ -98,8 +99,14 @@ "zone.js": "0.16.0" }, "peerDependencies": { + "msw": "^2", "typescript": ">=5.5.3 || 6.0.1-rc" }, + "peerDependenciesMeta": { + "msw": { + "optional": true + } + }, "engines": { "node": ">=20.19.0" } diff --git a/packages/openapi-ts/src/index.ts b/packages/openapi-ts/src/index.ts index 86681a22e0..a88edde21c 100644 --- a/packages/openapi-ts/src/index.ts +++ b/packages/openapi-ts/src/index.ts @@ -47,6 +47,7 @@ declare module '@hey-api/codegen-core' { | 'arktype' | 'fastify' | 'json-schema' + | 'msw' | 'sdk' | 'typescript' | 'valibot' @@ -80,6 +81,7 @@ declare module '@hey-api/shared' { '@tanstack/vue-query': Plugins.TanStackVueQuery.Types['Types']; arktype: Plugins.Arktype.Types['Types']; fastify: Plugins.Fastify.Types['Types']; + msw: Plugins.Msw.Types['Types']; nestjs: Plugins.NestJs.Types['Types']; swr: Plugins.Swr.Types['Types']; valibot: Plugins.Valibot.Types['Types']; @@ -141,6 +143,7 @@ import type { TanStackSvelteQueryPlugin } from './plugins/@tanstack/svelte-query import type { TanStackVueQueryPlugin } from './plugins/@tanstack/vue-query'; import type { ArktypePlugin } from './plugins/arktype'; import type { FastifyPlugin } from './plugins/fastify'; +import type { MswPlugin } from './plugins/msw'; import type { NestJsPlugin } from './plugins/nestjs'; import type { SwrPlugin } from './plugins/swr'; import type { ValibotPlugin, ValibotResolvers } from './plugins/valibot'; @@ -263,6 +266,10 @@ export namespace Plugins { export type Types = HeyApiTypeScriptPlugin; } + export namespace Msw { + export type Types = MswPlugin; + } + export namespace NestJs { export type Types = NestJsPlugin; } diff --git a/packages/openapi-ts/src/plugins/config.ts b/packages/openapi-ts/src/plugins/config.ts index 3dd51bcad0..1558458a68 100644 --- a/packages/openapi-ts/src/plugins/config.ts +++ b/packages/openapi-ts/src/plugins/config.ts @@ -22,6 +22,7 @@ import { defaultConfig as tanStackSvelteQuery } from '../plugins/@tanstack/svelt import { defaultConfig as tanStackVueQuery } from '../plugins/@tanstack/vue-query'; import { defaultConfig as arktype } from '../plugins/arktype'; import { defaultConfig as fastify } from '../plugins/fastify'; +import { defaultConfig as msw } from '../plugins/msw'; import { defaultConfig as nestjs } from '../plugins/nestjs'; import { defaultConfig as swr } from '../plugins/swr'; import { defaultConfig as valibot } from '../plugins/valibot'; @@ -52,6 +53,7 @@ export const defaultPluginConfigs: { '@tanstack/vue-query': tanStackVueQuery, arktype, fastify, + msw, nestjs, swr, valibot, diff --git a/packages/openapi-ts/src/plugins/msw/computeDominantResponse.ts b/packages/openapi-ts/src/plugins/msw/computeDominantResponse.ts new file mode 100644 index 0000000000..224db9fce0 --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/computeDominantResponse.ts @@ -0,0 +1,145 @@ +import { type IR, statusCodeToGroup } from '@hey-api/shared'; + +import type { MswPlugin } from './types'; + +export type ResponseKind = 'binary' | 'json' | 'text' | 'void'; + +export interface DominantResponse { + example: unknown; + kind: ResponseKind; + statusCode: number | undefined; +} + +const isValidExample = (example: unknown): boolean => { + if (example === undefined) { + return false; + } + if (example === null) { + return true; + } + const type = typeof example; + if (type === 'string' || type === 'number' || type === 'boolean') { + return true; + } + if (Array.isArray(example)) { + return example.every(isValidExample); + } + if (type === 'object') { + return Object.values(example as Record).every(isValidExample); + } + return false; +}; + +const KIND_PRIORITY: Record = { + binary: 1, + json: 3, + text: 2, + void: 0, +}; + +interface ResponseCandidate { + example: unknown; + kind: ResponseKind; + statusCode: number; +} + +const computeResponse = ({ + plugin, + response, + statusCode, +}: { + plugin: MswPlugin['Instance']; + response: IR.ResponseObject; + statusCode: string; +}): ResponseCandidate => { + const numericStatus = Number(statusCode); + + if (response.schema.type === 'void') { + return { example: undefined, kind: 'void', statusCode: numericStatus }; + } + + // In 2.0, empty responses get type 'unknown' (with inherited mediaType from + // `produces`). A bare 'unknown' without a $ref indicates no real content. + if (response.schema.type === 'unknown' && !response.schema.$ref) { + return { example: undefined, kind: 'void', statusCode: numericStatus }; + } + + let schema = response.schema; + if (schema.$ref) { + schema = plugin.context.resolveIrRef(schema.$ref); + } + + const example = isValidExample(schema.example) ? schema.example : undefined; + + if (schema.format === 'binary') { + return { example, kind: 'binary', statusCode: numericStatus }; + } + + const kind = mediaTypeToKind(response.mediaType); + return { example, kind, statusCode: numericStatus }; +}; + +const mediaTypeToKind = (mediaType: string | undefined): ResponseKind => { + if (!mediaType) { + return 'json'; + } + + const cleanMediaType = mediaType.split(';')[0]?.trim() ?? ''; + + if ( + cleanMediaType.startsWith('application/octet-stream') || + cleanMediaType.startsWith('audio/') || + cleanMediaType.startsWith('image/') || + cleanMediaType.startsWith('video/') + ) { + return 'binary'; + } + + if (cleanMediaType.startsWith('application/json') || cleanMediaType.endsWith('+json')) { + return 'json'; + } + + if (cleanMediaType.startsWith('text/')) { + return 'text'; + } + + // unknown media type, default to json + return 'json'; +}; + +export const computeDominantResponse = ({ + operation, + plugin, +}: { + operation: IR.OperationObject; + plugin: MswPlugin['Instance']; +}): DominantResponse => { + const candidates: Array = []; + + for (const statusCode in operation.responses) { + if (statusCodeToGroup({ statusCode }) !== '2XX') { + continue; + } + candidates.push( + computeResponse({ + plugin, + response: operation.responses[statusCode]!, + statusCode, + }), + ); + } + + if (candidates.length === 0) { + return { example: undefined, kind: 'void', statusCode: undefined }; + } + + const dominant = candidates.reduce((best, cur) => + KIND_PRIORITY[cur.kind] > KIND_PRIORITY[best.kind] ? cur : best, + ); + + return { + example: dominant.example, + kind: dominant.kind, + statusCode: dominant.statusCode, + }; +}; diff --git a/packages/openapi-ts/src/plugins/msw/config.ts b/packages/openapi-ts/src/plugins/msw/config.ts new file mode 100644 index 0000000000..fe00493720 --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/config.ts @@ -0,0 +1,20 @@ +import { definePluginConfig } from '@hey-api/shared'; + +import { handler } from './plugin'; +import type { MswPlugin } from './types'; + +export const defaultConfig: MswPlugin['Config'] = { + config: { + includeInEntry: false, + valueSources: ['example'], + }, + dependencies: ['@hey-api/typescript'], + handler, + name: 'msw', + tags: ['mocker'], +}; + +/** + * Type helper for `msw` plugin, returns {@link Plugin.Config} object + */ +export const defineConfig = definePluginConfig(defaultConfig); diff --git a/packages/openapi-ts/src/plugins/msw/handlerCreator.ts b/packages/openapi-ts/src/plugins/msw/handlerCreator.ts new file mode 100644 index 0000000000..a1998fe56a --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/handlerCreator.ts @@ -0,0 +1,399 @@ +import type { IR } from '@hey-api/shared'; + +import { $ } from '../../ts-dsl'; +import { computeDominantResponse, type DominantResponse } from './computeDominantResponse'; +import type { MswPlugin } from './types'; + +const emitStringifyPathParams = (plugin: MswPlugin['Instance']) => { + const symbol = plugin.symbol('StringifyPathParams', { + meta: { + category: 'type', + resource: 'stringify-path-params', + }, + }); + const stringifyPathParamsType = $.type + .alias(symbol) + .generic('T') + .type( + $.type + .mapped('K') + .key($.type('keyof T')) + .required() + .type( + $.type.or( + $.type('string'), + $.type('ReadonlyArray', (t) => t.generic($.type('string'))), + ), + ), + ); + plugin.node(stringifyPathParamsType); +}; + +const emitToResponseUnion = (plugin: MswPlugin['Instance']) => { + const symbol = plugin.symbol('ToResponseUnion', { + meta: { + category: 'type', + resource: 'to-response-union', + }, + }); + const extractKeyofTNumber = $.type('Extract', (t) => + t.generic($.type('keyof T')).generic($.type('number')), + ); + const toResponseUnionType = $.type + .alias(symbol) + .generic('T') + .type( + $.type.idx( + $.type + .mapped('K') + .key(extractKeyofTNumber) + .type( + $.type + .object() + .prop('status', (p) => p.type('K')) + .prop('result', (p) => p.type($.type.idx($.type('T'), $.type('K')))), + ), + $.type('Extract', (t) => t.generic($.type('keyof T')).generic($.type('number'))), + ), + ); + plugin.node(toResponseUnionType); +}; + +const emitHandlerFactory = (plugin: MswPlugin['Instance']) => { + const symbol = plugin.symbol('HttpHandlerFactory', { + meta: { + category: 'type', + resource: 'http-handler-factory', + }, + }); + const symbolHttpHandler = plugin.external('msw.HttpHandler'); + const symbolRequestHandlerOptions = plugin.external('msw.RequestHandlerOptions'); + const handlerFactoryType = $.type + .alias(symbol) + .generic('ResponseOrResolver') + .type( + $.type + .func() + .param('responseOrResolver', (p) => p.type('ResponseOrResolver')) + .param('options', (p) => p.type($.type(symbolRequestHandlerOptions)).optional()) + .returns($.type(symbolHttpHandler)), + ); + plugin.node(handlerFactoryType); +}; + +const emitOptionalParamHandlerFactory = (plugin: MswPlugin['Instance']) => { + const symbol = plugin.symbol('OptionalHttpHandlerFactory', { + meta: { + category: 'type', + resource: 'optional-http-handler-factory', + }, + }); + const symbolHttpHandler = plugin.external('msw.HttpHandler'); + const symbolRequestHandlerOptions = plugin.external('msw.RequestHandlerOptions'); + const optionalHandlerFactoryType = $.type + .alias(symbol) + .generic('ResponseOrResolver') + .type( + $.type + .func() + .param('responseOrResolver', (p) => p.type('ResponseOrResolver').optional()) + .param('options', (p) => p.type($.type(symbolRequestHandlerOptions)).optional()) + .returns($.type(symbolHttpHandler)), + ); + plugin.node(optionalHandlerFactoryType); +}; + +const httpMethodMap: Record = { + delete: 'delete', + get: 'get', + head: 'head', + options: 'options', + patch: 'patch', + post: 'post', + put: 'put', + trace: 'trace', +}; + +/** + * Builds the response override expression for the `res` parameter. + * When `res` is an object with a `status` property, it uses + * `res.result` as the value and `res.status` as the status code. + */ +const buildResponseOverrideExpr = ({ + dominantResponse: { kind: responseKind }, + responseOrFnName, + symbolHttpResponse, +}: { + dominantResponse: DominantResponse; + responseOrFnName: string; + symbolHttpResponse: ReturnType; +}) => { + const statusOption = $.object().prop('status', $.attr(responseOrFnName, 'status')); + const resultExpr = $.attr(responseOrFnName, 'result'); + + switch (responseKind) { + case 'void': { + return $.func((f) => + f.do( + $.new( + symbolHttpResponse, + $.binary(resultExpr, '??', $.literal(null)), + statusOption, + ).return(), + ), + ); + } + case 'json': { + return $.func((f) => + f.do( + $(symbolHttpResponse) + .attr('json') + .call($.binary(resultExpr, '??', $.literal(null)), statusOption) + .return(), + ), + ); + } + case 'text': { + return $.func((f) => + f.do( + $(symbolHttpResponse) + .attr('text') + .call($.binary(resultExpr, '??', $.literal(null)), statusOption) + .return(), + ), + ); + } + case 'binary': { + return $.func((f) => + f.do( + $.new( + symbolHttpResponse, + $.binary(resultExpr, '??', $.literal(null)), + statusOption, + ).return(), + ), + ); + } + } +}; + +/** + * Builds an arrow function that creates an MSW handler for a single operation. + * The response method and status code are inferred from the operation's responses. + */ +const createHandlerCreatorFn = ({ + dominantResponse, + hasResponseOverride, + method, + operation, + symbolHttp, + symbolHttpResponse, + symbolResolveToNull, + symbolToMswPath, +}: { + dominantResponse: DominantResponse; + hasResponseOverride: boolean; + method: string; + operation: IR.OperationObject; + symbolHttp: ReturnType; + symbolHttpResponse: ReturnType; + symbolResolveToNull: ReturnType; + symbolToMswPath: ReturnType; +}) => { + const responseOrFnName = 'res'; + const optionsName = 'options'; + + const fallbackTernary = $.ternary( + $.binary($.typeofExpr(responseOrFnName), '===', $.literal('function')), + ) + .do(responseOrFnName) + .otherwise($(symbolResolveToNull)); + + const resolverArg = hasResponseOverride + ? $.ternary( + $.binary( + $.binary($.typeofExpr(responseOrFnName), '===', $.literal('object')), + '&&', + $.attr(responseOrFnName, 'status'), + ), + ) + .do( + buildResponseOverrideExpr({ + dominantResponse, + responseOrFnName, + symbolHttpResponse, + }), + ) + .otherwise(fallbackTernary) + : fallbackTernary; + + const httpCall = $(symbolHttp) + .attr(method) + .call($.call(symbolToMswPath, $.literal(operation.path), 'baseUrl'), resolverArg, optionsName); + + return $.func((f) => { + if (dominantResponse.example != null && dominantResponse.statusCode != null) { + const status = dominantResponse.statusCode; + const example = dominantResponse.example; + f.param(responseOrFnName, (p) => p.assign($.fromValue({ result: example, status }))); + } else { + f.param(responseOrFnName); + } + f.param(optionsName); + f.do(httpCall.return()); + }); +}; + +export const operationToHandlerCreator = ({ + examples, + operation, + plugin, +}: { + examples: boolean; + operation: IR.OperationObject; + plugin: MswPlugin['Instance']; +}) => { + const method = httpMethodMap[operation.method]; + if (!method) { + return; + } + + const symbolHttp = plugin.external('msw.http'); + const symbolHttpResponse = plugin.external('msw.HttpResponse'); + const symbolHttpResponseResolver = plugin.external('msw.HttpResponseResolver'); + const symbolHttpHandlerFactory = plugin.referenceSymbol({ + category: 'type', + resource: 'http-handler-factory', + }); + const symbolOptionalHttpHandlerFactory = plugin.referenceSymbol({ + category: 'type', + resource: 'optional-http-handler-factory', + }); + const symbolStringifyPathParams = plugin.referenceSymbol({ + category: 'type', + resource: 'stringify-path-params', + }); + const symbolToResponseUnion = plugin.referenceSymbol({ + category: 'type', + resource: 'to-response-union', + }); + const symbolToMswPath = plugin.referenceSymbol({ + category: 'function', + resource: 'to-msw-path', + }); + const symbolResolveToNull = plugin.referenceSymbol({ + category: 'function', + resource: 'resolve-to-null', + }); + + // Query response type from @hey-api/typescript + const symbolResponsesType = plugin.querySymbol({ + category: 'type', + resource: 'operation', + resourceId: operation.id, + role: 'responses', + }); + let responsesOverrideType: ReturnType | undefined; + if (symbolResponsesType) { + if (!plugin.getSymbol({ category: 'type', resource: 'to-response-union' })) { + emitToResponseUnion(plugin); + } + responsesOverrideType = $.type(symbolToResponseUnion, (t) => t.generic(symbolResponsesType)); + } + + // Query data type for parameters + const symbolDataType = plugin.querySymbol({ + category: 'type', + resource: 'operation', + resourceId: operation.id, + role: 'data', + tool: 'typescript', + }); + + // Build HttpResponseResolver generics + const hasPathParams = + operation.parameters?.path && Object.keys(operation.parameters.path).length > 0; + const hasBody = !!operation.body; + + let pathParamsType: ReturnType | undefined; + if (hasPathParams && symbolDataType) { + if (!plugin.getSymbol({ category: 'type', resource: 'stringify-path-params' })) { + emitStringifyPathParams(plugin); + } + pathParamsType = $.type(symbolStringifyPathParams, (t) => + t.generic( + $.type('NonNullable', (t) => + t.generic($.type.idx($.type(symbolDataType), $.type.literal('path'))), + ), + ), + ); + } + + let bodyType: ReturnType | ReturnType | undefined; + if (hasBody && symbolDataType) { + bodyType = $.type.idx($.type(symbolDataType), $.type.literal('body')); + } + + // Build the resolver type: HttpResponseResolver + // Omit response type generic to avoid MSW's DefaultBodyType constraint issues + const hasResolverGenerics = pathParamsType || bodyType; + const resolverType = hasResolverGenerics + ? $.type(symbolHttpResponseResolver, (t) => + t.generics(pathParamsType ?? $.type('never'), bodyType ?? $.type('never')), + ) + : $.type(symbolHttpResponseResolver); + + const dominantResponse = computeDominantResponse({ operation, plugin }); + + // When examples are disabled, strip the example from the dominant response + if (!examples) { + dominantResponse.example = undefined; + } + + const handlerCreator = createHandlerCreatorFn({ + dominantResponse, + hasResponseOverride: dominantResponse.statusCode != null, + method, + operation, + symbolHttp, + symbolHttpResponse, + symbolResolveToNull, + symbolToMswPath, + }); + + const isOptional = + // if there is no dominantResponse, it means there is no status code definition + // so we can set the default response as null + !(dominantResponse.statusCode != null && responsesOverrideType) || + // if there is example, the param is optional because example can be used + // if it's void, the param is optional because we can define the default (`null`) + dominantResponse.example != null || + dominantResponse.kind === 'void'; + + const responseOrResolverType = + dominantResponse.statusCode != null && responsesOverrideType + ? $.type.or(responsesOverrideType, resolverType) + : resolverType; + + let handlerType: ReturnType; + if (isOptional) { + if (!plugin.getSymbol({ category: 'type', resource: 'optional-http-handler-factory' })) { + emitOptionalParamHandlerFactory(plugin); + } + handlerType = $.type(symbolOptionalHttpHandlerFactory, (t) => + t.generic(responseOrResolverType), + ); + } else { + if (!plugin.getSymbol({ category: 'type', resource: 'http-handler-factory' })) { + emitHandlerFactory(plugin); + } + handlerType = $.type(symbolHttpHandlerFactory, (t) => t.generic(responseOrResolverType)); + } + + return { + isOptional, + name: operation.id, + type: handlerType, + value: handlerCreator, + }; +}; diff --git a/packages/openapi-ts/src/plugins/msw/index.ts b/packages/openapi-ts/src/plugins/msw/index.ts new file mode 100644 index 0000000000..bccf6b6079 --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/index.ts @@ -0,0 +1,2 @@ +export { defaultConfig, defineConfig } from './config'; +export type { MswPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/msw/plugin.ts b/packages/openapi-ts/src/plugins/msw/plugin.ts new file mode 100644 index 0000000000..4afda409ea --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/plugin.ts @@ -0,0 +1,316 @@ +import { parseUrl } from '@hey-api/shared'; + +import { $ } from '../../ts-dsl'; +import { operationToHandlerCreator } from './handlerCreator'; +import type { MswPlugin } from './types'; + +export const handler: MswPlugin['Handler'] = ({ plugin }) => { + // Register external MSW symbols + plugin.symbol('http', { + external: 'msw', + meta: { + category: 'external', + resource: 'msw.http', + tool: 'msw', + }, + }); + + const symbolHttpHandler = plugin.symbol('HttpHandler', { + external: 'msw', + kind: 'type', + meta: { + category: 'external', + resource: 'msw.HttpHandler', + tool: 'msw', + }, + }); + + const symbolHttpResponse = plugin.symbol('HttpResponse', { + external: 'msw', + meta: { + category: 'external', + resource: 'msw.HttpResponse', + tool: 'msw', + }, + }); + + plugin.symbol('HttpResponseResolver', { + external: 'msw', + kind: 'type', + meta: { + category: 'external', + resource: 'msw.HttpResponseResolver', + tool: 'msw', + }, + }); + + plugin.symbol('RequestHandlerOptions', { + external: 'msw', + kind: 'type', + meta: { + category: 'external', + resource: 'msw.RequestHandlerOptions', + tool: 'msw', + }, + }); + + // Generate toMswPath helper + // const toMswPath = (path: string, baseUrl: string) => `${baseUrl}${path.replace(/\{([^}]+)\}/g, ':$1')}` + const symbolToMswPath = plugin.symbol('toMswPath', { + meta: { + category: 'function', + resource: 'to-msw-path', + }, + }); + const toMswPathFn = $.const(symbolToMswPath).assign( + $.func((f) => + f + .param('path', (p) => p.type('string')) + .param('baseUrl', (p) => p.type('string')) + .do( + $.return( + $.template($('baseUrl')).add( + $('path').attr('replace').call($.regexp('\\{([^}]+)\\}', 'g'), $.literal(':$1')), + ), + ), + ), + ), + ); + plugin.node(toMswPathFn); + + // Generate resolveToNull helper + // const resolveToNull = () => new HttpResponse(null) + const symbolResolveToNull = plugin.symbol('resolveToNull', { + meta: { + category: 'function', + resource: 'resolve-to-null', + }, + }); + const resolveToNullFn = $.const(symbolResolveToNull).assign( + $.func((f) => f.do($.new(symbolHttpResponse, $.literal(null)).return())), + ); + plugin.node(resolveToNullFn); + + // Resolve default baseUrl from spec servers + let defaultBaseUrl = ''; + const { servers } = plugin.context.ir; + const firstServer = servers?.[0]; + if (firstServer) { + const serverUrl = firstServer.url; + const url = parseUrl(serverUrl); + if (url.protocol && url.host && !serverUrl.includes('{')) { + defaultBaseUrl = serverUrl; + } else if (serverUrl !== '/' && serverUrl.startsWith('/')) { + defaultBaseUrl = serverUrl.endsWith('/') ? serverUrl.slice(0, -1) : serverUrl; + } + } + + // Generate createMswHandlerFactory + const symbolFactory = plugin.symbol('createMswHandlerFactory'); + const ofObject = $.object().pretty(); + const singleHandlerFactoriesType = $.type.object(); + const handlerMeta: Array<{ isOptional: boolean; name: string }> = []; + + plugin.forEach( + 'operation', + ({ operation }) => { + const handlerCreator = operationToHandlerCreator({ + examples: plugin.config.valueSources?.includes('example') ?? true, + operation, + plugin, + }); + if (handlerCreator) { + ofObject.prop(handlerCreator.name, handlerCreator.value); + singleHandlerFactoriesType.prop(handlerCreator.name, (p) => p.type(handlerCreator.type)); + handlerMeta.push({ isOptional: handlerCreator.isOptional, name: handlerCreator.name }); + } + }, + { + order: 'declarations', + }, + ); + + // Emit SingleHandlerFactories type + const symbolSingleHandlerFactories = plugin.symbol('SingleHandlerFactories'); + plugin.node($.type.alias(symbolSingleHandlerFactories).export().type(singleHandlerFactoriesType)); + + // Emit OfAllOptions type + const symbolOfAllOptions = plugin.symbol('OfAllOptions'); + plugin.node( + $.type + .alias(symbolOfAllOptions) + .export() + .type( + $.type + .object() + .prop('onMissingMock', (p) => + p.required(false).type($.type.or($.type.literal('error'), $.type.literal('skip'))), + ) + .prop('overrides', (p) => + p.required(false).type( + $.type + .mapped('K') + .key($.type.operator().keyof($.type(symbolSingleHandlerFactories))) + .optional() + .type( + $.type.idx( + $.type('Parameters', (t) => + t.generic($.type.idx($.type(symbolSingleHandlerFactories), $.type('K'))), + ), + $.type.literal(0), + ), + ), + ), + ), + ), + ); + + // Emit MswHandlerFactory type + const symbolMswHandlerFactory = plugin.symbol('MswHandlerFactory'); + plugin.node( + $.type + .alias(symbolMswHandlerFactory) + .export() + .type( + $.type + .object() + .prop('of', (p) => p.type(symbolSingleHandlerFactories)) + .prop('ofAll', (p) => + p.type( + $.type + .func() + .param('options', (pp) => pp.type($.type(symbolOfAllOptions)).optional()) + .returns($.type('Array', (t) => t.generic($.type(symbolHttpHandler)))), + ), + ), + ), + ); + + // Build ofAll function body + const ofAllBodyStmts: Array = []; + const hasRequiredHandlers = handlerMeta.some((h) => !h.isOptional); + + if (hasRequiredHandlers) { + ofAllBodyStmts.push( + $.const('onMissingMock').assign( + $.binary($.attr($('options'), 'onMissingMock').optional(), '??', $.literal('skip')), + ), + ); + } + + ofAllBodyStmts.push($.const('overrides').assign($.attr($('options'), 'overrides').optional())); + + ofAllBodyStmts.push( + $.const('handlers') + .type($.type('Array', (t) => t.generic($.type(symbolHttpHandler)))) + .assign($.array()), + ); + + // Generate addRequiredHandler helper when there are required handlers + if (hasRequiredHandlers) { + const errorResolver = $.func((ef) => + ef.do( + $.new( + symbolHttpResponse, + $.literal('[heyapi-msw] The mock of this request is not implemented.'), + $.object().prop('status', $.literal(501)), + ).return(), + ), + ); + + // handler: (value: Value | (() => HttpResponse)) => HttpHandler + const handlerParamType = $.type + .func() + .param('value', (pp) => + pp.type( + $.type.or( + 'Value', + $.type.func().returns($.type(symbolHttpResponse, (t) => t.generic('any'))), + ), + ), + ) + .returns($.type(symbolHttpHandler)); + + ofAllBodyStmts.push( + $.const('addRequiredHandler').assign( + $.func((f) => + f + .generic('Value') + .param('handler', (p) => p.type(handlerParamType)) + .param('override', (p) => p.type($.type.or('Value', 'undefined'))) + .do( + $.if($.binary($('override'), '!=', $.literal(null))) + .do( + $.stmt( + $('handlers') + .attr('push') + .call($('handler').call($('override'))), + ), + ) + .otherwise( + $.if($.binary($('onMissingMock'), '===', $.literal('error'))).do( + $.stmt($('handlers').attr('push').call($('handler').call(errorResolver))), + ), + ), + ), + ), + ), + ); + } + + for (const handler of handlerMeta) { + if (handler.isOptional) { + ofAllBodyStmts.push( + $.stmt( + $('handlers') + .attr('push') + .call( + $('of') + .attr(handler.name) + .call($.attr($('overrides'), handler.name).optional()), + ), + ), + ); + } else { + ofAllBodyStmts.push( + $.stmt( + $('addRequiredHandler').call( + $('of').attr(handler.name), + $.attr($('overrides'), handler.name).optional(), + ), + ), + ); + } + } + + ofAllBodyStmts.push($.return($('handlers'))); + + const ofAllFn = $.func((f) => { + f.param('options', (p) => p.required(false).type($.type(symbolOfAllOptions))); + f.returns($.type('Array', (t) => t.generic($.type(symbolHttpHandler)))); + f.do(...ofAllBodyStmts); + }); + + const factoryFn = $.const(symbolFactory) + .export() + .assign( + $.func((f) => + f + .param('config', (p) => + p + .required(false) + .type($.type.object().prop('baseUrl', (p) => p.required(false).type('string'))), + ) + .returns($.type(symbolMswHandlerFactory)) + .do( + $.const('baseUrl').assign( + $.binary($.attr($('config'), 'baseUrl').optional(), '??', $.literal(defaultBaseUrl)), + ), + $.const('of').type('SingleHandlerFactories').assign(ofObject), + $.const('ofAll').assign(ofAllFn), + $.return($.object().prop('of', 'of').prop('ofAll', 'ofAll')), + ), + ), + ); + plugin.node(factoryFn); +}; diff --git a/packages/openapi-ts/src/plugins/msw/types.ts b/packages/openapi-ts/src/plugins/msw/types.ts new file mode 100644 index 0000000000..e91ad3a845 --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/types.ts @@ -0,0 +1,17 @@ +import type { DefinePlugin, Plugin } from '@hey-api/shared'; + +export type UserConfig = Plugin.Name<'msw'> & + Plugin.Hooks & + Plugin.UserExports & { + /** + * Sources for inferring default parameter values in generated handler + * factories. Order determines priority (earlier entries take precedence). + * + * - `'example'` - use OpenAPI example values + * + * @default ['example'] + */ + valueSources?: Array<'example'>; + }; + +export type MswPlugin = DefinePlugin; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b2ed05345..4a01cef8cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,7 +48,7 @@ importers: version: 7.0.0-dev.20260312.1 '@vitest/coverage-v8': specifier: 4.1.0 - version: 4.1.0(vitest@4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) + version: 4.1.0(vitest@4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) eslint: specifier: 9.39.2 version: 9.39.2(jiti@2.6.1) @@ -96,7 +96,7 @@ importers: version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) vitest: specifier: 4.1.0 - version: 4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) dev: devDependencies: @@ -142,6 +142,9 @@ importers: arktype: specifier: 2.2.0 version: 2.2.0 + msw: + specifier: 2.10.2 + version: 2.10.2(@types/node@25.2.1)(typescript@5.9.3) nuxt: specifier: 3.21.0 version: 3.21.0(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@25.2.1)(@vue/compiler-sfc@3.5.27)(cac@6.7.14)(db0@0.3.4)(encoding@0.1.13)(eslint@9.39.2(jiti@2.6.1))(ioredis@5.9.2)(less@4.4.2)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup@4.56.0)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.4(typescript@5.9.3))(yaml@2.8.2) @@ -230,7 +233,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: 21.1.2 - version: 21.1.2(b9fcfc698b20164011953e0b05a5c087) + version: 21.1.2(f1f0187b9a28baac9b02f8c93bfba118) '@angular/cli': specifier: 21.1.2 version: 21.1.2(@types/node@24.10.10)(chokidar@5.0.0)(hono@4.11.8) @@ -324,7 +327,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: 21.1.2 - version: 21.1.2(e7024e150686e3bbe2cdedb5695d6284) + version: 21.1.2(a3942970943f268369c4c88439aad5ea) '@angular/cli': specifier: 21.1.2 version: 21.1.2(@types/node@24.10.10)(chokidar@5.0.0)(hono@4.11.8) @@ -467,7 +470,7 @@ importers: version: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: 4.0.18 - version: 4.0.18(@types/node@25.2.1)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 4.0.18(@types/node@25.2.1)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) examples/openapi-ts-fetch: dependencies: @@ -517,6 +520,9 @@ importers: eslint-plugin-react-refresh: specifier: 0.4.7 version: 0.4.7(eslint@9.17.0(jiti@2.6.1)) + msw: + specifier: 2.10.2 + version: 2.10.2(@types/node@25.2.1)(typescript@5.9.3) oxfmt: specifier: 0.27.0 version: 0.27.0 @@ -532,6 +538,9 @@ importers: vite: specifier: 7.3.1 version: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vitest: + specifier: 4.0.18 + version: 4.0.18(@types/node@25.2.1)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) examples/openapi-ts-ky: dependencies: @@ -647,7 +656,7 @@ importers: version: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: ^4.0.18 - version: 4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) examples/openapi-ts-next: dependencies: @@ -785,7 +794,7 @@ importers: version: 8.0.2(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.13(typescript@5.9.3)) vitest: specifier: 4.0.18 - version: 4.0.18(@types/node@24.10.10)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 4.0.18(@types/node@24.10.10)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vue-tsc: specifier: 3.2.4 version: 3.2.4(typescript@5.9.3) @@ -940,7 +949,7 @@ importers: version: 8.0.2(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.13(typescript@5.9.3)) vitest: specifier: 4.0.18 - version: 4.0.18(@types/node@24.10.10)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 4.0.18(@types/node@24.10.10)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vue-tsc: specifier: 3.2.4 version: 3.2.4(typescript@5.9.3) @@ -992,7 +1001,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: 21.1.2 - version: 21.1.2(bfb22c0d38474de5255b56daa3b8edae) + version: 21.1.2(d1d14c4ec45bbc59bdd6f28c4606c248) '@angular/cli': specifier: 21.1.2 version: 21.1.2(@types/node@25.2.1)(chokidar@5.0.0)(hono@4.11.8) @@ -1153,7 +1162,7 @@ importers: version: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: 4.0.18 - version: 4.0.18(@types/node@25.2.1)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 4.0.18(@types/node@25.2.1)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) examples/openapi-ts-tanstack-vue-query: dependencies: @@ -1238,7 +1247,7 @@ importers: version: 8.0.2(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.13(typescript@5.9.3)) vitest: specifier: 4.0.18 - version: 4.0.18(@types/node@24.10.10)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 4.0.18(@types/node@24.10.10)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vue-tsc: specifier: 3.2.4 version: 3.2.4(typescript@5.9.3) @@ -1325,7 +1334,7 @@ importers: version: 3.16.2 '@nuxt/test-utils': specifier: 4.0.0 - version: 4.0.0(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) + version: 4.0.0(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) vite: specifier: 7.3.1 version: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) @@ -1439,6 +1448,9 @@ importers: ky: specifier: 1.14.3 version: 1.14.3 + msw: + specifier: 2.10.2 + version: 2.10.2(@types/node@25.2.1)(typescript@5.9.3) nuxt: specifier: 3.14.1592 version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@25.2.1)(db0@0.3.4)(encoding@0.1.13)(eslint@9.39.1(jiti@2.6.1))(ioredis@5.9.2)(less@4.4.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup@4.56.0)(sass@1.97.1)(terser@5.44.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.4(typescript@5.9.3)) @@ -1465,7 +1477,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: 21.1.2 - version: 21.1.2(bfb22c0d38474de5255b56daa3b8edae) + version: 21.1.2(d1d14c4ec45bbc59bdd6f28c4606c248) '@angular/animations': specifier: 21.1.2 version: 21.1.2(@angular/core@21.1.2(@angular/compiler@21.1.2)(rxjs@7.8.2)(zone.js@0.16.0)) @@ -1550,6 +1562,9 @@ importers: ky: specifier: 1.14.3 version: 1.14.3 + msw: + specifier: 2.10.2 + version: 2.10.2(@types/node@25.2.1)(typescript@5.9.3) node-fetch: specifier: 3.3.2 version: 3.3.2 @@ -2733,6 +2748,15 @@ packages: '@braidai/lang@1.1.2': resolution: {integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==} + '@bundled-es-modules/cookie@2.0.1': + resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + '@changesets/apply-release-plan@7.1.0': resolution: {integrity: sha512-yq8ML3YS7koKQ/9bk1PqO0HMzApIFNwjlwCnwFEXMzNe8NpzeeYYKCmnhWJGkN8g7E51MnWaSbqRcTcdIxUgnQ==} @@ -4651,6 +4675,10 @@ packages: cpu: [x64] os: [win32] + '@mswjs/interceptors@0.39.8': + resolution: {integrity: sha512-2+BzZbjRO7Ct61k8fMNHEtoKjeWI9pIlHFTqBwZ5icHpqszIgEZbjb1MW5Z0+bITTCTl3gk4PDBxs9tA/csXvA==} + engines: {node: '>=18'} + '@napi-rs/nice-android-arm-eabi@1.1.1': resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} engines: {node: '>= 10'} @@ -5128,6 +5156,15 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@opencode-ai/sdk@1.2.25': resolution: {integrity: sha512-ikuGWob48OM7LTgfXFqGOZKVOqh50FEjvtIBhXGhGowJhifmjZ+xuq/ypP8nPjTwUX73pbu1C3X9ZBWVkCN9mA==} @@ -7583,6 +7620,9 @@ packages: '@types/sockjs@0.3.36': resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} @@ -10708,6 +10748,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql@16.13.1: + resolution: {integrity: sha512-gGgrVCoDKlIZ8fIqXBBb0pPKqDgki0Z/FSKNiQzSGj2uEYHr1tq5wmBegGwJx6QB5S5cM0khSBpi/JFHMCvsmQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gray-matter@4.0.3: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} @@ -10770,6 +10814,9 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -11128,6 +11175,9 @@ packages: resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==} engines: {node: '>=16'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -12122,6 +12172,16 @@ packages: msgpackr@1.11.5: resolution: {integrity: sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==} + msw@2.10.2: + resolution: {integrity: sha512-RCKM6IZseZQCWcSWlutdf590M8nVfRHG1ImwzOtwz8IYxgT4zhUO0rfTcTvDGiaFE0Rhcc+h43lcF3Jc9gFtwQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} @@ -12568,6 +12628,9 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} @@ -13275,6 +13338,9 @@ packages: prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} @@ -13307,6 +13373,9 @@ packages: quansync@1.0.0: resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -14114,6 +14183,9 @@ packages: streamx@2.22.1: resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -14475,6 +14547,10 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + tough-cookie@6.0.0: resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} engines: {node: '>=16'} @@ -14865,6 +14941,10 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -15102,6 +15182,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + urlpattern-polyfill@10.1.0: resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} @@ -15634,10 +15717,6 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - watchpack@2.4.4: - resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} - engines: {node: '>=10.13.0'} - watchpack@2.5.0: resolution: {integrity: sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==} engines: {node: '>=10.13.0'} @@ -16078,13 +16157,13 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@21.1.2(b9fcfc698b20164011953e0b05a5c087)': + '@angular-devkit/build-angular@21.1.2(a3942970943f268369c4c88439aad5ea)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) '@angular-devkit/build-webpack': 0.2101.2(chokidar@5.0.0)(webpack-dev-server@5.2.2(tslib@2.8.1)(webpack@5.104.1(@swc/core@1.15.18)(esbuild@0.27.2)))(webpack@5.104.1(@swc/core@1.15.18)(esbuild@0.27.2)) '@angular-devkit/core': 21.1.2(chokidar@5.0.0) - '@angular/build': 21.1.2(715bbcce77c47a418ef6cc48ef7ededc) + '@angular/build': 21.1.2(fa3d27d2bfba2058220c00fd3fb69db1) '@angular/compiler-cli': 21.1.2(@angular/compiler@21.1.2)(typescript@5.9.3) '@babel/core': 7.28.5 '@babel/generator': 7.28.5 @@ -16167,13 +16246,13 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-angular@21.1.2(bfb22c0d38474de5255b56daa3b8edae)': + '@angular-devkit/build-angular@21.1.2(d1d14c4ec45bbc59bdd6f28c4606c248)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) '@angular-devkit/build-webpack': 0.2101.2(chokidar@5.0.0)(webpack-dev-server@5.2.2(tslib@2.8.1)(webpack@5.104.1(@swc/core@1.15.18)(esbuild@0.27.2)))(webpack@5.104.1(@swc/core@1.15.18)(esbuild@0.27.2)) '@angular-devkit/core': 21.1.2(chokidar@5.0.0) - '@angular/build': 21.1.2(4ae04ca60d5e69e646f2e6985d14766b) + '@angular/build': 21.1.2(ca7d45c5c89e8f8891af69ebb1866aaf) '@angular/compiler-cli': 21.1.2(@angular/compiler@21.1.2)(typescript@5.9.3) '@babel/core': 7.28.5 '@babel/generator': 7.28.5 @@ -16256,13 +16335,13 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-angular@21.1.2(e7024e150686e3bbe2cdedb5695d6284)': + '@angular-devkit/build-angular@21.1.2(f1f0187b9a28baac9b02f8c93bfba118)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) '@angular-devkit/build-webpack': 0.2101.2(chokidar@5.0.0)(webpack-dev-server@5.2.2(tslib@2.8.1)(webpack@5.104.1(@swc/core@1.15.18)(esbuild@0.27.2)))(webpack@5.104.1(@swc/core@1.15.18)(esbuild@0.27.2)) '@angular-devkit/core': 21.1.2(chokidar@5.0.0) - '@angular/build': 21.1.2(b8a91db99523b9ce3edfd9b854e0f81b) + '@angular/build': 21.1.2(59f40ca61b5d02419ec13dbea9cd06eb) '@angular/compiler-cli': 21.1.2(@angular/compiler@21.1.2)(typescript@5.9.3) '@babel/core': 7.28.5 '@babel/generator': 7.28.5 @@ -16380,7 +16459,7 @@ snapshots: '@angular/core': 21.1.2(@angular/compiler@21.1.2)(rxjs@7.8.2)(zone.js@0.16.0) tslib: 2.8.1 - '@angular/build@21.1.2(4ae04ca60d5e69e646f2e6985d14766b)': + '@angular/build@21.1.2(59f40ca61b5d02419ec13dbea9cd06eb)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) @@ -16389,8 +16468,8 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@25.2.1) - '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.3.0(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@inquirer/confirm': 5.1.21(@types/node@24.10.10) + '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) beasties: 0.3.5 browserslist: 4.28.1 esbuild: 0.27.2 @@ -16411,7 +16490,7 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.18.2 - vite: 7.3.0(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) watchpack: 2.5.0 optionalDependencies: '@angular/core': 21.1.2(@angular/compiler@21.1.2)(rxjs@7.8.2)(zone.js@0.16.0) @@ -16422,8 +16501,8 @@ snapshots: less: 4.4.2 lmdb: 3.4.4 postcss: 8.5.6 - tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.2.1)(typescript@5.9.3)) - vitest: 4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@24.10.10)(typescript@5.9.3)) + vitest: 4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - '@types/node' - chokidar @@ -16437,7 +16516,7 @@ snapshots: - tsx - yaml - '@angular/build@21.1.2(715bbcce77c47a418ef6cc48ef7ededc)': + '@angular/build@21.1.2(ca7d45c5c89e8f8891af69ebb1866aaf)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) @@ -16446,8 +16525,8 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@24.10.10) - '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@inquirer/confirm': 5.1.21(@types/node@25.2.1) + '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.3.0(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) beasties: 0.3.5 browserslist: 4.28.1 esbuild: 0.27.2 @@ -16468,7 +16547,7 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.18.2 - vite: 7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) watchpack: 2.5.0 optionalDependencies: '@angular/core': 21.1.2(@angular/compiler@21.1.2)(rxjs@7.8.2)(zone.js@0.16.0) @@ -16479,8 +16558,8 @@ snapshots: less: 4.4.2 lmdb: 3.4.4 postcss: 8.5.6 - tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@24.10.10)(typescript@5.9.3)) - vitest: 4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.2.1)(typescript@5.9.3)) + vitest: 4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - '@types/node' - chokidar @@ -16494,7 +16573,7 @@ snapshots: - tsx - yaml - '@angular/build@21.1.2(b8a91db99523b9ce3edfd9b854e0f81b)': + '@angular/build@21.1.2(fa3d27d2bfba2058220c00fd3fb69db1)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) @@ -16537,7 +16616,7 @@ snapshots: lmdb: 3.4.4 postcss: 8.5.6 tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@24.10.10)(typescript@5.9.3)) - vitest: 4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - '@types/node' - chokidar @@ -17748,6 +17827,19 @@ snapshots: '@braidai/lang@1.1.2': {} + '@bundled-es-modules/cookie@2.0.1': + dependencies: + cookie: 0.7.2 + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.2 + + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + '@changesets/apply-release-plan@7.1.0': dependencies: '@changesets/config': 3.1.3 @@ -19379,6 +19471,15 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': optional: true + '@mswjs/interceptors@0.39.8': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + '@napi-rs/nice-android-arm-eabi@1.1.1': optional: true @@ -20262,7 +20363,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/test-utils@4.0.0(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))': + '@nuxt/test-utils@4.0.0(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))': dependencies: '@clack/prompts': 1.0.0 '@nuxt/devtools-kit': 2.7.0(magicast@0.3.5)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -20291,12 +20392,12 @@ snapshots: tinyexec: 1.0.2 ufo: 1.6.3 unplugin: 3.0.0 - vitest-environment-nuxt: 1.0.1(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) + vitest-environment-nuxt: 1.0.1(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) vue: 3.5.27(typescript@5.9.3) optionalDependencies: '@vue/test-utils': 2.4.6 jsdom: 28.0.0 - vitest: 4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - crossws - magicast @@ -20548,6 +20649,15 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + '@opencode-ai/sdk@1.2.25': {} '@oxc-minify/binding-android-arm-eabi@0.110.0': @@ -22589,6 +22699,8 @@ snapshots: dependencies: '@types/node': 24.10.10 + '@types/statuses@2.0.6': {} + '@types/tough-cookie@4.0.5': {} '@types/triple-beam@1.3.5': {} @@ -23144,7 +23256,7 @@ snapshots: vite: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vue: 3.5.27(typescript@5.9.3) - '@vitest/coverage-v8@4.1.0(vitest@4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))': + '@vitest/coverage-v8@4.1.0(vitest@4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.1.0 @@ -23156,7 +23268,7 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/expect@4.0.18': dependencies: @@ -23176,45 +23288,50 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: + msw: 2.10.2(@types/node@24.10.10)(typescript@5.9.3) vite: 7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: + msw: 2.10.2(@types/node@25.2.1)(typescript@5.9.3) vite: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.1.0(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.1.0(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: + msw: 2.10.2(@types/node@24.10.10)(typescript@5.9.3) vite: 7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) optional: true - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.1.0(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: + msw: 2.10.2(@types/node@24.10.10)(typescript@5.9.3) vite: 7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.1.0(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: + msw: 2.10.2(@types/node@25.2.1)(typescript@5.9.3) vite: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@4.0.18': @@ -24795,7 +24912,7 @@ snapshots: dependencies: glob-parent: 6.0.2 normalize-path: 3.0.0 - schema-utils: 4.3.2 + schema-utils: 4.3.3 serialize-javascript: 6.0.2 tinyglobby: 0.2.15 webpack: 5.104.1(@swc/core@1.15.18)(esbuild@0.27.2) @@ -26744,6 +26861,8 @@ snapshots: graphemer@1.4.0: {} + graphql@16.13.1: {} + gray-matter@4.0.3: dependencies: js-yaml: 3.14.1 @@ -26828,6 +26947,8 @@ snapshots: dependencies: '@types/hast': 3.0.4 + headers-polyfill@4.0.3: {} + highlight.js@10.7.3: {} hono@4.11.8: {} @@ -27199,6 +27320,8 @@ snapshots: is-network-error@1.1.0: {} + is-node-process@1.2.0: {} + is-number-object@1.1.1: dependencies: call-bound: 1.0.4 @@ -28217,8 +28340,8 @@ snapshots: mini-css-extract-plugin@2.9.4(webpack@5.104.1(@swc/core@1.15.18)(esbuild@0.27.2)): dependencies: - schema-utils: 4.3.2 - tapable: 2.2.3 + schema-utils: 4.3.3 + tapable: 2.3.0 webpack: 5.104.1(@swc/core@1.15.18)(esbuild@0.27.2) minimalistic-assert@1.0.1: {} @@ -28365,6 +28488,57 @@ snapshots: msgpackr-extract: 3.0.3 optional: true + msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.21(@types/node@24.10.10) + '@mswjs/interceptors': 0.39.8 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.6 + graphql: 16.13.1 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.41.0 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + optional: true + + msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.21(@types/node@25.2.1) + '@mswjs/interceptors': 0.39.8 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.6 + graphql: 16.13.1 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.41.0 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + muggle-string@0.4.1: {} multer@2.1.1: @@ -29494,6 +29668,8 @@ snapshots: outdent@0.5.0: {} + outvariant@1.4.3: {} + own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 @@ -30292,6 +30468,10 @@ snapshots: prr@1.0.1: optional: true + psl@1.15.0: + dependencies: + punycode: 2.3.1 + pump@3.0.3: dependencies: end-of-stream: 1.4.5 @@ -30317,6 +30497,8 @@ snapshots: quansync@1.0.0: {} + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} quick-format-unescaped@4.0.4: {} @@ -31363,6 +31545,8 @@ snapshots: optionalDependencies: bare-events: 2.6.1 + strict-event-emitter@0.5.1: {} + string-argv@0.3.2: {} string-width@4.2.3: @@ -31853,6 +32037,13 @@ snapshots: totalist@3.0.1: {} + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + tough-cookie@6.0.0: dependencies: tldts: 7.0.22 @@ -32375,6 +32566,8 @@ snapshots: universalify@0.1.2: {} + universalify@0.2.0: {} + universalify@2.0.1: {} unixify@1.0.0: @@ -32659,6 +32852,11 @@ snapshots: dependencies: punycode: 2.3.1 + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + urlpattern-polyfill@10.1.0: {} urlpattern-polyfill@8.0.2: {} @@ -33122,9 +33320,9 @@ snapshots: - universal-cookie - yaml - vitest-environment-nuxt@1.0.1(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))): + vitest-environment-nuxt@1.0.1(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))): dependencies: - '@nuxt/test-utils': 4.0.0(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) + '@nuxt/test-utils': 4.0.0(@vue/test-utils@2.4.6)(jsdom@28.0.0)(magicast@0.3.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) transitivePeerDependencies: - '@cucumber/cucumber' - '@jest/globals' @@ -33141,10 +33339,10 @@ snapshots: - vite - vitest - vitest@4.0.18(@types/node@24.10.10)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.0.18(@types/node@24.10.10)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -33179,10 +33377,10 @@ snapshots: - tsx - yaml - vitest@4.0.18(@types/node@25.2.1)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.0.18(@types/node@25.2.1)(jiti@2.6.1)(jsdom@28.0.0)(less@4.4.2)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -33217,10 +33415,10 @@ snapshots: - tsx - yaml - vitest@4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): + vitest@4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.1.0(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -33234,7 +33432,7 @@ snapshots: picomatch: 4.0.3 std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 vite: 7.3.0(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) @@ -33246,10 +33444,10 @@ snapshots: - msw optional: true - vitest@4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): + vitest@4.1.0(@types/node@24.10.10)(jsdom@28.0.0)(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.1.0(msw@2.10.2(@types/node@24.10.10)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -33263,7 +33461,7 @@ snapshots: picomatch: 4.0.3 std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 vite: 7.3.1(@types/node@24.10.10)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) @@ -33274,10 +33472,10 @@ snapshots: transitivePeerDependencies: - msw - vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): + vitest@4.1.0(@types/node@25.2.1)(jsdom@28.0.0)(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.1.0(msw@2.10.2(@types/node@25.2.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -33291,7 +33489,7 @@ snapshots: picomatch: 4.0.3 std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 vite: 7.3.1(@types/node@25.2.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) @@ -33448,11 +33646,6 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - watchpack@2.4.4: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - watchpack@2.5.0: dependencies: glob-to-regexp: 0.4.1 @@ -33563,7 +33756,7 @@ snapshots: schema-utils: 4.3.3 tapable: 2.3.0 terser-webpack-plugin: 5.3.16(@swc/core@1.15.18)(esbuild@0.27.2)(webpack@5.104.1(@swc/core@1.15.18)(esbuild@0.27.2)) - watchpack: 2.4.4 + watchpack: 2.5.0 webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' diff --git a/specs/2.0.x/response-example.yaml b/specs/2.0.x/response-example.yaml new file mode 100644 index 0000000000..542075891c --- /dev/null +++ b/specs/2.0.x/response-example.yaml @@ -0,0 +1,54 @@ +swagger: 2.0 +info: + title: OpenAPI 2.0 body response application/json inline example + version: 1 +paths: + /foo: + post: + consumes: + - text/plain + produces: + - application/json + parameters: + - name: body + in: body + required: true + schema: + type: string + responses: + '200': + description: OK + schema: + type: object + properties: + fullName: + type: string + age: + type: number + example: + fullName: 'John Doe' + age: 34 + '204': + description: SUCCESSFUL + get: + produces: + - application/json + responses: + '200': + description: OK + schema: + $ref: '#/definitions/Person' +definitions: + Person: + type: object + properties: + firstName: + type: string + lastName: + type: string + age: + type: number + example: + firstName: 'Marry' + lastName: 'Jane' + age: 30 diff --git a/specs/3.0.x/response-example.yaml b/specs/3.0.x/response-example.yaml new file mode 100644 index 0000000000..2fd4275202 --- /dev/null +++ b/specs/3.0.x/response-example.yaml @@ -0,0 +1,53 @@ +openapi: 3.0.4 +info: + title: OpenAPI 3.0.4 body response application/json inline example + version: 1 +paths: + /foo: + post: + requestBody: + content: + 'text/plain': + schema: + type: string + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + fullName: + type: string + age: + type: number + example: + fullName: 'John Doe' + age: 34 + '204': + description: SUCCESSFUL + get: + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Person' +components: + schemas: + Person: + type: object + properties: + firstName: + type: string + lastName: + type: string + age: + type: number + example: + firstName: 'Marry' + lastName: 'Jane' + age: 30 diff --git a/specs/3.0.x/response-types.yaml b/specs/3.0.x/response-types.yaml new file mode 100644 index 0000000000..7d21afd1fd --- /dev/null +++ b/specs/3.0.x/response-types.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.4 +info: + title: OpenAPI 3.0.4 response types + version: '1' +paths: + /foo: + get: + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: + name: 'Alice' + text/plain: + schema: + type: string + application/octet-stream: + schema: + type: string + format: binary diff --git a/specs/3.1.x/response-example.yaml b/specs/3.1.x/response-example.yaml new file mode 100644 index 0000000000..1f8df45c75 --- /dev/null +++ b/specs/3.1.x/response-example.yaml @@ -0,0 +1,53 @@ +openapi: 3.1.1 +info: + title: OpenAPI 3.1.1 body response application/json inline example + version: 1 +paths: + /foo: + post: + requestBody: + content: + 'text/plain': + schema: + type: string + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + fullName: + type: string + age: + type: number + example: + fullName: 'John Doe' + age: 34 + '204': + description: SUCCESSFUL + get: + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Person' +components: + schemas: + Person: + type: object + properties: + firstName: + type: string + lastName: + type: string; + age: + type: number + example: + firstName: 'Marry' + lastName: 'Jane' + age: 30 diff --git a/specs/3.1.x/response-types.yaml b/specs/3.1.x/response-types.yaml new file mode 100644 index 0000000000..fde681eb30 --- /dev/null +++ b/specs/3.1.x/response-types.yaml @@ -0,0 +1,26 @@ +openapi: 3.1.0 +info: + title: OpenAPI 3.1.0 response types + version: '1' +paths: + /foo: + get: + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: + name: 'Alice' + text/plain: + schema: + type: string + application/octet-stream: + schema: + type: string + format: binary