From ab4d5bcbdd990ee44657c4ca58e70722f78284cc Mon Sep 17 00:00:00 2001 From: Jorge Junior Date: Sun, 7 Sep 2025 11:44:53 -0300 Subject: [PATCH 1/2] :sparkles: feat(openapi): enhance specPath handling for absolute and relative URLs in OpenAPI integration tests --- src/index.ts | 20 +++++++++++++++++++- test/index.test.ts | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index a13eaf1..9600f6f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,7 +38,25 @@ export const openapi = < ...documentation.info } - const relativePath = specPath.startsWith('/') ? specPath.slice(1) : specPath + // Determine the correct URL for the OpenAPI spec + // Use absolute path to avoid browser URL resolution issues when paths have complex hierarchies + const getSpecUrl = () => { + if (!specPath.startsWith('/')) { + // Already relative + return specPath + } + + // For default case where specPath follows the pattern path + '/json', use relative path + const defaultSpecPath = `${path}/json` + if (specPath === defaultSpecPath) { + return specPath.startsWith('/') ? specPath.slice(1) : specPath + } + + // For custom specPath, use absolute path to prevent browser URL resolution issues + return specPath + } + + const relativePath = getSpecUrl() let totalRoutes = 0 let cachedSchema: OpenAPIV3.Document | undefined diff --git a/test/index.test.ts b/test/index.test.ts index e9593c0..b1f96da 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,9 +1,9 @@ -import { Elysia, t } from 'elysia' import SwaggerParser from '@apidevtools/swagger-parser' +import { Elysia, t } from 'elysia' import { openapi } from '../src' -import { describe, expect, it } from 'bun:test' import { fail } from 'assert' +import { describe, expect, it } from 'bun:test' const req = (path: string) => new Request(`http://localhost${path}`) @@ -272,4 +272,44 @@ describe('Swagger', () => { const response = await res.json() expect(Object.keys(response.paths['/all'])).toBeArrayOfSize(8) }) + + it('should use absolute path for custom specPath to prevent path duplication', async () => { + const app = new Elysia().use( + openapi({ + path: '/api/v1/docs', + specPath: '/api/v1/openapi.json' + }) + ) + + await app.modules + + const res = await app.handle(req('/api/v1/docs')).then((x) => x.text()) + + // The data-url should be the absolute path to prevent duplication + expect(res.includes('data-url="/api/v1/openapi.json"')).toBe(true) + + // Ensure the spec endpoint works + const specRes = await app.handle(req('/api/v1/openapi.json')) + expect(specRes.status).toBe(200) + }) + + it('should use relative path for default specPath pattern', async () => { + const app = new Elysia().use( + openapi({ + path: '/api/docs' + // specPath defaults to '/api/docs/json' + }) + ) + + await app.modules + + const res = await app.handle(req('/api/docs')).then((x) => x.text()) + + // The data-url should be relative for default pattern + expect(res.includes('data-url="api/docs/json"')).toBe(true) + + // Ensure the spec endpoint works + const specRes = await app.handle(req('/api/docs/json')) + expect(specRes.status).toBe(200) + }) }) From 82ec3503348838c8e22905f6a0688befb6fdd8af Mon Sep 17 00:00:00 2001 From: Jorge Junior Date: Sun, 7 Sep 2025 12:04:12 -0300 Subject: [PATCH 2/2] :sparkles: refactor(openapi): rename variable for clarity in spec URL handling --- src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9600f6f..b069ff0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -56,7 +56,7 @@ export const openapi = < return specPath } - const relativePath = getSpecUrl() + const specUrl = getSpecUrl() let totalRoutes = 0 let cachedSchema: OpenAPIV3.Document | undefined @@ -70,14 +70,14 @@ export const openapi = < new Response( provider === 'swagger-ui' ? SwaggerUIRender(info, { - url: relativePath, + url: specUrl, dom_id: '#swagger-ui', version: 'latest', autoDarkMode: true, ...swagger }) : ScalarRender(info, { - url: relativePath, + url: specUrl, version: 'latest', cdn: `https://cdn.jsdelivr.net/npm/@scalar/api-reference@${scalar?.version ?? 'latest'}/dist/browser/standalone.min.js`, ...(scalar as ApiReferenceConfiguration),