From 288d6c7aab5e26a8d78dc563f9d0d7eb69bc3173 Mon Sep 17 00:00:00 2001 From: DocFast CEO Date: Wed, 25 Feb 2026 13:04:26 +0000 Subject: [PATCH] fix: revert swagger-jsdoc to 6.2.8 (7.0.0-rc.6 broke OpenAPI spec generation) + add OpenAPI spec tests swagger-jsdoc 7.0.0-rc.6 returns empty spec (0 paths), breaking /docs and /openapi.json. Reverted to 6.2.8 which correctly generates all 10+ paths. Added 2 regression tests to catch this in CI. --- package-lock.json | 65 ++++++++++++++++++++++++++------------- package.json | 2 +- src/__tests__/api.test.ts | 21 +++++++++++++ 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49e2634..99652fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "pino": "^10.3.1", "puppeteer": "^24.0.0", "stripe": "^20.3.1", - "swagger-jsdoc": "^7.0.0-rc.6", + "swagger-jsdoc": "^6.2.8", "swagger-ui-dist": "^5.31.0" }, "devDependencies": { @@ -63,9 +63,9 @@ "license": "MIT" }, "node_modules/@apidevtools/swagger-parser": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.2.tgz", - "integrity": "sha512-JFxcEyp8RlNHgBCE98nwuTkZT6eNFPc1aosWV6wPcQph72TSEEu1k3baJD4/x1qznU+JiDdz8F5pTwabZh+Dhg==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.6", @@ -73,7 +73,7 @@ "@apidevtools/swagger-methods": "^3.0.2", "@jsdevtools/ono": "^7.1.3", "call-me-maybe": "^1.0.1", - "z-schema": "^4.2.3" + "z-schema": "^5.0.1" }, "peerDependencies": { "openapi-types": ">=7" @@ -1713,7 +1713,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/compressible": { @@ -4002,21 +4002,34 @@ } }, "node_modules/swagger-jsdoc": { - "version": "7.0.0-rc.6", - "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-7.0.0-rc.6.tgz", - "integrity": "sha512-LIvIPQxipRaOzIij+HrWOcCWTINE6OeJuqmXCfDkofVcstPVABHRkaAc3D7vrX9s7L0ccH0sH0amwHgN6+SXPg==", + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", "license": "MIT", "dependencies": { + "commander": "6.2.0", "doctrine": "3.0.0", "glob": "7.1.6", - "lodash.mergewith": "4.6.2", - "swagger-parser": "10.0.2", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", "yaml": "2.0.0-1" }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, "engines": { "node": ">=12.0.0" } }, + "node_modules/swagger-jsdoc/node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/swagger-jsdoc/node_modules/yaml": { "version": "2.0.0-1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", @@ -4027,12 +4040,12 @@ } }, "node_modules/swagger-parser": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.2.tgz", - "integrity": "sha512-9jHkHM+QXyLGFLk1DkXBwV+4HyNm0Za3b8/zk/+mjr8jgOSiqm3FOTHBSDsBjtn9scdL+8eWcHdupp2NLM8tDw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", "license": "MIT", "dependencies": { - "@apidevtools/swagger-parser": "10.0.2" + "@apidevtools/swagger-parser": "10.0.3" }, "engines": { "node": ">=10" @@ -4644,23 +4657,33 @@ } }, "node_modules/z-schema": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.4.tgz", - "integrity": "sha512-YvBeW5RGNeNzKOUJs3rTL4+9rpcvHXt5I051FJbOcitV8bl40pEfcG0Q+dWSwS0/BIYrMZ/9HHoqLllMkFhD0w==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", "license": "MIT", "dependencies": { "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", - "validator": "^13.6.0" + "validator": "^13.7.0" }, "bin": { "z-schema": "bin/z-schema" }, "engines": { - "node": ">=6.0.0" + "node": ">=8.0.0" }, "optionalDependencies": { - "commander": "^2.7.1" + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" } }, "node_modules/zod": { diff --git a/package.json b/package.json index bffa2a5..e9db8d0 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "pino": "^10.3.1", "puppeteer": "^24.0.0", "stripe": "^20.3.1", - "swagger-jsdoc": "^7.0.0-rc.6", + "swagger-jsdoc": "^6.2.8", "swagger-ui-dist": "^5.31.0" }, "devDependencies": { diff --git a/src/__tests__/api.test.ts b/src/__tests__/api.test.ts index 5954ea7..d173297 100644 --- a/src/__tests__/api.test.ts +++ b/src/__tests__/api.test.ts @@ -585,6 +585,27 @@ describe("Rate limit headers on PDF endpoints", () => { }); }); +describe("OpenAPI spec", () => { + it("returns a valid OpenAPI 3.0 spec with paths", async () => { + const res = await fetch(`${BASE}/openapi.json`); + expect(res.status).toBe(200); + const spec = await res.json(); + expect(spec.openapi).toBe("3.0.3"); + expect(spec.info).toBeDefined(); + expect(spec.info.title).toBe("DocFast API"); + expect(Object.keys(spec.paths).length).toBeGreaterThanOrEqual(8); + }); + + it("includes all major endpoint groups", async () => { + const res = await fetch(`${BASE}/openapi.json`); + const spec = await res.json(); + const paths = Object.keys(spec.paths); + expect(paths).toContain("/v1/convert/html"); + expect(paths).toContain("/v1/convert/markdown"); + expect(paths).toContain("/health"); + }); +}); + describe("404 handler", () => { it("returns proper JSON error format for API routes", async () => { const res = await fetch(`${BASE}/v1/nonexistent-endpoint`);