From 26af4047ac04fc99b07c2df5b9cb8823530afc18 Mon Sep 17 00:00:00 2001 From: Sean Ferguson Date: Tue, 21 Jul 2026 12:15:08 -0400 Subject: [PATCH] fix codegen client compatibility Co-authored-by: Codex --- README.md | 6 +++--- package.json | 2 +- src/client/types.ts | 10 +++++----- test/e2e/e2e.test.ts | 14 ++++++++++++-- test/e2e/petstore.yaml | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 03adf54..d7ad320 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ configureOpenApiClient(client, { Authorization: `Bearer ${token}` }, onError: (err, options) => { - if (err instanceof OpenApiError && err.response.status === 401) { + if (err instanceof OpenApiError && err.response?.status === 401) { redirectToLogin(); return null; // the request promise will never settle } @@ -168,14 +168,14 @@ Called when the server returns an error response. Receives an `OpenApiError` and configureOpenApiClient(client, { onError: (err, options) => { // Throw a different error - if (err.response.status === 403) { + if (err.response?.status === 403) { throw new ForbiddenError(err.message); } // Return null to silently discard the error — the request promise // will never resolve or reject, so the caller hangs and does nothing. // Useful when you're redirecting the user away from the page entirely. - if (err.response.status === 401) { + if (err.response?.status === 401) { redirectToLogin(); return null; } diff --git a/package.json b/package.json index 01c5bca..711eca2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zyno-io/openapi-client-codegen", - "version": "2.9.0", + "version": "2.9.1", "description": "OpenAPI client generator for TypeScript apps", "license": "MIT", "author": { diff --git a/src/client/types.ts b/src/client/types.ts index abaeb83..c201512 100644 --- a/src/client/types.ts +++ b/src/client/types.ts @@ -36,16 +36,16 @@ export interface OpenApiRequestOptions { [key: string]: unknown; } -export type RequestResult = ThrowOnError extends true - ? Promise<{ data: TData; request: Request; response: Response }> - : Promise< - ({ data: TData; error: undefined } | { data: undefined; error: TError }) & { +export type RequestResult = Promise< + ThrowOnError extends true + ? { data: TData; request: Request; response: Response } + : ({ data: TData; error: undefined } | { data: undefined; error: TError }) & { // request/response may be undefined, because the error may originate from // building the request object itself or from a network error request?: Request; response?: Response; } - >; +>; export interface OpenApiClient { setConfig(config: Record): unknown; diff --git a/test/e2e/e2e.test.ts b/test/e2e/e2e.test.ts index 4380daa..a20edad 100644 --- a/test/e2e/e2e.test.ts +++ b/test/e2e/e2e.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert/strict'; -import { existsSync, rmSync } from 'node:fs'; +import { existsSync, readFileSync, rmSync } from 'node:fs'; import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'; import path from 'node:path'; import { describe, it, before } from 'node:test'; @@ -46,10 +46,20 @@ describe('E2E: OpenAPI Client Codegen', () => { const { client } = await import('./generated/client.gen.js'); configureOpenApiClient(client, { headers: { 'X-Test': 'value' }, - onError: () => null + onError: () => null, + wrapper: async (options, request) => { + return await request(options); + } }); }); + it('uses JSON when an optional multipart upload is omitted', () => { + const sdk = readFileSync(path.join(OUT_PATH, 'sdk.gen.ts'), 'utf8'); + const createRequestMethod = sdk.slice(sdk.indexOf('public static createRequest')); + + assert.match(createRequestMethod, /'Content-Type': 'application\/json'/); + }); + it('converts native Blob and File values to Deepkit multipart payloads', async () => { const blob = new Blob(['blob-content'], { type: 'text/plain' }); const file = new File(['file-content'], 'file.txt', { type: 'text/plain' }); diff --git a/test/e2e/petstore.yaml b/test/e2e/petstore.yaml index 4a1d196..84a18ba 100644 --- a/test/e2e/petstore.yaml +++ b/test/e2e/petstore.yaml @@ -57,6 +57,24 @@ paths: $ref: '#/components/schemas/Pet' '404': description: Pet not found + /requests: + post: + operationId: createRequest + tags: + - requests + summary: Create a request with an optional photo + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/CreateRequestRequest' + application/json: + schema: + $ref: '#/components/schemas/CreateRequestRequest' + responses: + '204': + description: Request created components: schemas: Pet: @@ -93,3 +111,21 @@ components: type: string name: type: string + CreateRequestRequest: + type: object + required: + - description + - serviceAddress + properties: + description: + type: string + serviceAddress: + type: object + required: + - city + properties: + city: + type: string + photo: + type: string + format: binary