diff --git a/package-lock.json b/package-lock.json index 24d1240..beb33b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "kiattp", - "version": "0.1.0", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kiattp", - "version": "0.1.0", + "version": "0.3.0", "license": "MIT", "devDependencies": { "@types/node": "^25.5.2", diff --git a/src/instance.ts b/src/instance.ts index 97e2c87..24cbd95 100644 --- a/src/instance.ts +++ b/src/instance.ts @@ -23,7 +23,8 @@ export function createInstance(defaults?: Partial): Instance { ...merged, url: fullUrl, }); - return request(instanceConfig.url!, instanceConfig, instanceChain); + const { baseURL: _baseURL, url: _url, ...resolvedInstanceConfig } = instanceConfig; + return request(instanceConfig.url!, resolvedInstanceConfig, instanceChain); }; const methodFn = (method: HttpMethod): RequestFn => { diff --git a/test/axios/compatibility.test.ts b/test/axios/compatibility.test.ts index de08e35..36d7e49 100644 --- a/test/axios/compatibility.test.ts +++ b/test/axios/compatibility.test.ts @@ -15,6 +15,10 @@ const server = setupServer( await new Promise((resolve) => setTimeout(resolve, 1000)); return HttpResponse.json([{ id: 1 }]); }), + http.post('https://api.example.com/api/auth/login', async ({ request: req }) => { + const body = await req.json(); + return HttpResponse.json({ token: 'abc', ...body }); + }), ); beforeAll(() => server.listen()); @@ -74,4 +78,10 @@ describe('axios compatibility', () => { ]); expect(results).toHaveLength(2); }); + + it('axios.create() with baseURL containing a path prepends full baseURL when path starts with /', async () => { + const api = axios.create({ baseURL: 'https://api.example.com/api' }); + const res = await api.post('/auth/login', { email: 'test@test.com', password: 'test' }); + expect(res.data).toMatchObject({ token: 'abc', email: 'test@test.com' }); + }); }); diff --git a/test/unit/instance-baseurl.test.ts b/test/unit/instance-baseurl.test.ts new file mode 100644 index 0000000..5331f28 --- /dev/null +++ b/test/unit/instance-baseurl.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { createInstance } from '../../src/instance'; + +describe('baseURL + leading slash path (relative baseURL)', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('does not double-prefix baseURL when path starts with /', async () => { + const capturedUrls: string[] = []; + + // Spy on global fetch to capture the URL actually used + vi.stubGlobal('fetch', vi.fn(async (url: string) => { + capturedUrls.push(url); + return new Response(JSON.stringify({ ok: true }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + })); + + const instance = createInstance({ baseURL: '/api' }); + await instance.post('/auth/login', { body: { email: 'test@test.com' } }); + + expect(capturedUrls).toHaveLength(1); + // Should be /api/auth/login, NOT /api/api/auth/login + expect(capturedUrls[0]).toBe('/api/auth/login'); + }); + + it('does not double-prefix baseURL when path does not start with /', async () => { + const capturedUrls: string[] = []; + + vi.stubGlobal('fetch', vi.fn(async (url: string) => { + capturedUrls.push(url); + return new Response(JSON.stringify({ ok: true }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + })); + + const instance = createInstance({ baseURL: '/api' }); + await instance.get('users'); + + expect(capturedUrls).toHaveLength(1); + expect(capturedUrls[0]).toBe('/api/users'); + }); +});