diff --git a/.eslintrc b/.eslintrc index 586e6d313..cd833ac4f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,4 @@ { "extends": ["@readme/eslint-config", "@readme/eslint-config/typescript", "@readme/eslint-config/esm"], - "root": true + "root": true, } diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..ab0b9d7a6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +packages/parser/test/specs/large-file-memory-leak/cloudflare-stringified.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..98e77de7f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll": "explicit" + }, + "editor.formatOnSave": true +} diff --git a/package-lock.json b/package-lock.json index ab5a7da4f..669042070 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "./packages/*" ], "devDependencies": { - "@readme/eslint-config": "^14.0.0", + "@readme/eslint-config": "^14.2.0", "@vitest/coverage-v8": "^3.0.4", "alex": "^11.0.1", "eslint": "^8.57.0", @@ -34,6 +34,23 @@ "node": ">=6.0.0" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.9.0", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.0.tgz", + "integrity": "sha512-8Q/r5mXLa8Rfyh6r4SgEEFJgISVN5cDNFlcfSWLgFn3odzQhTfHAqzI3hMGdcROViL+8NrDNVVFQtEUrYOksDg==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, "node_modules/@babel/code-frame": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", @@ -236,9 +253,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", "cpu": [ "ppc64" ], @@ -253,9 +270,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", "cpu": [ "arm" ], @@ -270,9 +287,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", "cpu": [ "arm64" ], @@ -287,9 +304,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", "cpu": [ "x64" ], @@ -304,9 +321,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", "cpu": [ "arm64" ], @@ -321,9 +338,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", "cpu": [ "x64" ], @@ -338,9 +355,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", "cpu": [ "arm64" ], @@ -355,9 +372,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", "cpu": [ "x64" ], @@ -372,9 +389,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", "cpu": [ "arm" ], @@ -389,9 +406,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", "cpu": [ "arm64" ], @@ -406,9 +423,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", "cpu": [ "ia32" ], @@ -423,9 +440,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", "cpu": [ "loong64" ], @@ -440,9 +457,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", "cpu": [ "mips64el" ], @@ -457,9 +474,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", "cpu": [ "ppc64" ], @@ -474,9 +491,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", "cpu": [ "riscv64" ], @@ -491,9 +508,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", "cpu": [ "s390x" ], @@ -508,9 +525,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", "cpu": [ "x64" ], @@ -542,9 +559,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", "cpu": [ "x64" ], @@ -559,9 +576,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", "cpu": [ "arm64" ], @@ -576,9 +593,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", "cpu": [ "x64" ], @@ -593,9 +610,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", "cpu": [ "x64" ], @@ -610,9 +627,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", "cpu": [ "arm64" ], @@ -627,9 +644,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", "cpu": [ "ia32" ], @@ -644,9 +661,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", "cpu": [ "x64" ], @@ -2230,16 +2247,16 @@ } }, "node_modules/@readme/eslint-config": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@readme/eslint-config/-/eslint-config-14.1.2.tgz", - "integrity": "sha512-KhdKYK9GBxA36YAYx6jNIbs4/n/zxKEnEfs3XvVbEwmtkojlGdF+oWQlnzelZeBgKwh0/Je2cD2haauC7186Pw==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@readme/eslint-config/-/eslint-config-14.2.0.tgz", + "integrity": "sha512-C1d2E38xfdze39gP4JejIjO/XHxPbb8j1pbY6BRlwKoeQOUCojdEP5mVcUMexTjtASAgbm4yJY5Mj2RGievNFw==", "dev": true, "license": "ISC", "dependencies": { "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^9.1.0", + "eslint-config-prettier": "^10.0.1", "eslint-import-resolver-typescript": "^3.5.5", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.28.1", @@ -2249,13 +2266,13 @@ "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-react": "^7.34.4", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-readme": "^2.0.6", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-readme": "^2.0.7", "eslint-plugin-require-extensions": "^0.1.3", - "eslint-plugin-testing-library": "^6.0.1", + "eslint-plugin-testing-library": "^7.1.1", "eslint-plugin-try-catch-failsafe": "^0.1.4", "eslint-plugin-typescript-sort-keys": "^3.2.0", - "eslint-plugin-unicorn": "^55.0.0", + "eslint-plugin-unicorn": "^56.0.1", "eslint-plugin-vitest": "^0.5.4", "eslint-plugin-you-dont-need-lodash-underscore": "^6.12.0", "lodash": "^4.17.21" @@ -2830,6 +2847,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "3.0.15", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", @@ -3690,20 +3714,6 @@ "dev": true, "license": "MIT" }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -4074,19 +4084,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -4250,9 +4247,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -4270,10 +4267,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4554,9 +4551,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001669", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", - "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "version": "1.0.30001699", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", + "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", "dev": true, "funding": [ { @@ -4681,41 +4678,19 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" } }, "node_modules/chownr": { @@ -5839,13 +5814,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.23.3" + "browserslist": "^4.24.3" }, "funding": { "type": "opencollective", @@ -6473,9 +6448,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.41", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz", - "integrity": "sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==", + "version": "1.5.97", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.97.tgz", + "integrity": "sha512-HKLtaH02augM7ZOdYRuO19rWDeY+QSJ1VxnXFa/XDFLf07HvM90pALIJFgrO+UVaajI3+aJMMpojoUTLZyQ7JQ==", "dev": true, "license": "ISC" }, @@ -6833,9 +6808,9 @@ } }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6846,30 +6821,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, "node_modules/escalade": { @@ -6995,13 +6971,13 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz", + "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==", "dev": true, "license": "MIT", "bin": { - "eslint-config-prettier": "bin/cli.js" + "eslint-config-prettier": "build/bin/cli.js" }, "peerDependencies": { "eslint": ">=7.0.0" @@ -7442,16 +7418,16 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { @@ -7520,9 +7496,9 @@ } }, "node_modules/eslint-plugin-readme": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-readme/-/eslint-plugin-readme-2.0.6.tgz", - "integrity": "sha512-BOGxzzTzAoIIccSPR8f1paYNuC+3Pz9LNNXK3zoDW5Wt5QEdTUvef/RXh0gEVp8hH70bkchAR2h8V71w36vkHw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-readme/-/eslint-plugin-readme-2.0.7.tgz", + "integrity": "sha512-Hqs26rR3P+X6y1sIL80lMpbD58rOMMXGlHOZBIc4i0bcO1WE9dvnYBpc06kTbZCF0dyEUL9YwRo/GGy6K6XE+Q==", "dev": true, "license": "ISC", "engines": { @@ -7546,34 +7522,35 @@ } }, "node_modules/eslint-plugin-testing-library": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.4.0.tgz", - "integrity": "sha512-yeWF+YgCgvNyPNI9UKnG0FjeE2sk93N/3lsKqcmR8dSfeXJwFT5irnWo7NjLf152HkRzfoFjh3LsBUrhvFz4eA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-7.1.1.tgz", + "integrity": "sha512-nszC833aZPwB6tik1nMkbFqmtgIXTT0sfJEYs0zMBKMlkQ4to2079yUV96SvmLh00ovSBJI4pgcBC1TiIP8mXg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^5.62.0" + "@typescript-eslint/scope-manager": "^8.15.0", + "@typescript-eslint/utils": "^8.15.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0", - "npm": ">=6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0", + "pnpm": "^9.14.0" }, "peerDependencies": { - "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz", + "integrity": "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -7581,13 +7558,13 @@ } }, "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.0.tgz", + "integrity": "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==", "dev": true, "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -7595,100 +7572,98 @@ } }, "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz", + "integrity": "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.0.tgz", + "integrity": "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.24.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/typescript-estree": "8.24.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz", + "integrity": "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "8.24.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-plugin-testing-library/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/eslint-plugin-testing-library/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=8.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-plugin-testing-library/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/eslint-plugin-testing-library/node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, "node_modules/eslint-plugin-try-catch-failsafe": { @@ -7722,19 +7697,19 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "55.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz", - "integrity": "sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA==", + "version": "56.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.1.tgz", + "integrity": "sha512-FwVV0Uwf8XPfVnKSGpMg7NtlZh0G0gBarCaFcMUOoqPxXryxdYxTRRv4kH6B9TFCVIrjRXG+emcxIk2ayZilog==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.7", "@eslint-community/eslint-utils": "^4.4.0", "ci-info": "^4.0.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.37.0", - "esquery": "^1.5.0", - "globals": "^15.7.0", + "core-js-compat": "^3.38.1", + "esquery": "^1.6.0", + "globals": "^15.9.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.1", "jsesc": "^3.0.2", @@ -7742,7 +7717,7 @@ "read-pkg-up": "^7.0.1", "regexp-tree": "^0.1.27", "regjsparser": "^0.10.0", - "semver": "^7.6.1", + "semver": "^7.6.3", "strip-indent": "^3.0.0" }, "engines": { @@ -7756,9 +7731,9 @@ } }, "node_modules/eslint-plugin-unicorn/node_modules/globals": { - "version": "15.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", - "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", "dev": true, "license": "MIT", "engines": { @@ -10349,19 +10324,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -11249,9 +11211,9 @@ } }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -14159,9 +14121,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, @@ -14196,16 +14158,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/normalize-url": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", @@ -16231,16 +16183,17 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", "dev": true, "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/redent": { @@ -18464,27 +18417,27 @@ "license": "0BSD" }, "node_modules/tsup": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.3.0.tgz", - "integrity": "sha512-ALscEeyS03IomcuNdFdc0YWGVIkwH1Ws7nfTbAPuoILvEV2hpGQAY72LIOjglGo4ShWpZfpBqP/jpQVCzqYQag==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.3.6.tgz", + "integrity": "sha512-XkVtlDV/58S9Ye0JxUUTcrQk4S+EqlOHKzg6Roa62rdjL1nGWNUstG0xgI4vanHdfIpjP448J8vlN0oK6XOJ5g==", "dev": true, "license": "MIT", "dependencies": { "bundle-require": "^5.0.0", "cac": "^6.7.14", - "chokidar": "^3.6.0", + "chokidar": "^4.0.1", "consola": "^3.2.3", - "debug": "^4.3.5", - "esbuild": "^0.23.0", - "execa": "^5.1.1", + "debug": "^4.3.7", + "esbuild": "^0.24.0", "joycon": "^3.1.1", - "picocolors": "^1.0.1", + "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", - "rollup": "^4.19.0", + "rollup": "^4.24.0", "source-map": "0.8.0-beta.0", "sucrase": "^3.35.0", - "tinyglobby": "^0.2.1", + "tinyexec": "^0.3.1", + "tinyglobby": "^0.2.9", "tree-kill": "^1.2.2" }, "bin": { @@ -18515,30 +18468,6 @@ } } }, - "node_modules/tsup/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, "node_modules/tsup/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -19252,9 +19181,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -19273,7 +19202,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -19695,469 +19624,788 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", - "cpu": [ - "ppc64" - ], + "node_modules/vitest": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.4.tgz", + "integrity": "sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "dependencies": { + "@vitest/expect": "3.0.4", + "@vitest/mocker": "3.0.4", + "@vitest/pretty-format": "^3.0.4", + "@vitest/runner": "3.0.4", + "@vitest/snapshot": "3.0.4", + "@vitest/spy": "3.0.4", + "@vitest/utils": "3.0.4", + "chai": "^5.1.2", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, "engines": { - "node": ">=18" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.4", + "@vitest/ui": "3.0.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", - "cpu": [ - "arm" - ], + "node_modules/walk-up-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", + "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } + "license": "ISC" }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", - "cpu": [ - "arm64" - ], + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "defaults": "^1.0.3" } }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", - "cpu": [ - "x64" - ], + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", - "cpu": [ - "x64" - ], + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, "engines": { - "node": ">=18" + "node": ">= 8" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", - "cpu": [ - "arm64" - ], + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", - "cpu": [ - "x64" - ], + "node_modules/which-builtin-type": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", + "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } + "dependencies": { + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", - "cpu": [ - "arm" - ], + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", - "cpu": [ - "arm64" - ], + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", - "cpu": [ - "ia32" - ], + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", - "cpu": [ - "loong64" - ], + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", - "cpu": [ - "mips64el" - ], + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "string-width": "^5.0.1" + }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", - "cpu": [ - "ppc64" - ], + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", - "cpu": [ - "riscv64" - ], + "node_modules/widest-line/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", - "cpu": [ - "s390x" - ], + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", - "cpu": [ - "x64" - ], + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=0.10.0" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", - "cpu": [ - "x64" - ], + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", - "cpu": [ - "arm64" - ], + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=18" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", - "cpu": [ - "x64" - ], + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/write-json-file": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz", + "integrity": "sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.15", + "make-dir": "^2.1.0", + "pify": "^4.0.1", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.4.2" + }, "engines": { - "node": ">=18" + "node": ">=6" } }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", - "cpu": [ - "x64" - ], + "node_modules/write-json-file/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, "engines": { - "node": ">=18" + "node": ">=6" } }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", - "cpu": [ - "arm64" - ], + "node_modules/write-json-file/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": ">=6" } }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", - "cpu": [ - "ia32" - ], + "node_modules/write-json-file/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/write-json-file/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/write-pkg": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz", + "integrity": "sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "sort-keys": "^2.0.0", + "type-fest": "^0.4.1", + "write-json-file": "^3.2.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", - "cpu": [ - "x64" - ], + "node_modules/write-pkg/node_modules/type-fest": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", + "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=6" + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "dev": true, + "license": "ISC", "bin": { - "esbuild": "bin/esbuild" + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "packages/oas": { + "version": "25.3.0", + "license": "MIT", + "dependencies": { + "@readme/json-schema-ref-parser": "^1.2.0", + "@types/json-schema": "^7.0.11", + "json-schema-merge-allof": "^0.8.1", + "jsonpath-plus": "^10.0.0", + "jsonpointer": "^5.0.0", + "memoizee": "^0.4.16", + "openapi-types": "^12.1.1", + "path-to-regexp": "^8.1.0", + "remove-undefined-objects": "^6.0.0" + }, + "devDependencies": { + "@readme/oas-examples": "^5.13.0", + "@readme/openapi-parser": "file:../parser", + "@types/json-schema-merge-allof": "^0.6.5", + "@types/memoizee": "^0.4.11", + "@types/node": "^22.7.6", + "tsup": "^8.0.2", + "typescript": "^5.4.4", + "vitest": "^3.0.5" }, "engines": { "node": ">=18" + } + }, + "packages/oas-normalize": { + "version": "12.1.0", + "license": "MIT", + "dependencies": { + "@readme/openapi-parser": "file:../parser", + "@readme/postman-to-openapi": "^4.1.0", + "js-yaml": "^4.1.0", + "openapi-types": "^12.1.3", + "swagger2openapi": "^7.0.8" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "devDependencies": { + "@readme/oas-examples": "^5.12.0", + "@types/js-yaml": "^4.0.9", + "@types/swagger2openapi": "^7.0.4", + "eslint": "^8.57.0", + "nock": "^14.0.0", + "tsup": "^8.0.2", + "typescript": "^5.1.6", + "vitest": "^3.0.5" + }, + "engines": { + "node": ">=18" + } + }, + "packages/oas-normalize/node_modules/@vitest/expect": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", + "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/oas-normalize/node_modules/@vitest/mocker": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", + "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "packages/oas-normalize/node_modules/@vitest/pretty-format": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", + "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/oas-normalize/node_modules/@vitest/runner": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", + "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.0.5", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/oas-normalize/node_modules/@vitest/snapshot": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", + "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.5", + "magic-string": "^0.30.17", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/oas-normalize/node_modules/@vitest/spy": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", + "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/oas-normalize/node_modules/@vitest/utils": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", + "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.5", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/oas-normalize/node_modules/vite-node": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", + "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.2", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/vitest": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.4.tgz", - "integrity": "sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==", + "packages/oas-normalize/node_modules/vitest": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", + "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.0.4", - "@vitest/mocker": "3.0.4", - "@vitest/pretty-format": "^3.0.4", - "@vitest/runner": "3.0.4", - "@vitest/snapshot": "3.0.4", - "@vitest/spy": "3.0.4", - "@vitest/utils": "3.0.4", + "@vitest/expect": "3.0.5", + "@vitest/mocker": "3.0.5", + "@vitest/pretty-format": "^3.0.5", + "@vitest/runner": "3.0.5", + "@vitest/snapshot": "3.0.5", + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", "chai": "^5.1.2", "debug": "^4.4.0", "expect-type": "^1.1.0", @@ -20169,7 +20417,7 @@ "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.4", + "vite-node": "3.0.5", "why-is-node-running": "^2.3.0" }, "bin": { @@ -20185,8 +20433,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.4", - "@vitest/ui": "3.0.4", + "@vitest/browser": "3.0.5", + "@vitest/ui": "3.0.5", "happy-dom": "*", "jsdom": "*" }, @@ -20214,662 +20462,839 @@ } } }, - "node_modules/walk-up-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", - "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", - "dev": true, - "license": "ISC" + "packages/oas-to-har": { + "name": "@readme/oas-to-har", + "version": "24.0.7", + "license": "ISC", + "dependencies": { + "@readme/data-urls": "^3.0.0", + "oas": "file:../oas", + "qs": "^6.12.0", + "remove-undefined-objects": "^6.0.0" + }, + "devDependencies": { + "@readme/oas-examples": "^5.12.0", + "@types/har-format": "^1.2.15", + "@types/qs": "^6.9.14", + "eslint": "^8.57.0", + "jest-expect-har": "^7.1.0", + "tsup": "^8.0.2", + "type-fest": "^4.18.3", + "typescript": "^5.2.2", + "vitest": "^3.0.5" + }, + "engines": { + "node": ">=18" + } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "packages/oas-to-har/node_modules/@vitest/expect": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", + "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", "dev": true, "license": "MIT", "dependencies": { - "defaults": "^1.0.3" + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "packages/oas-to-har/node_modules/@vitest/mocker": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", + "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", "dev": true, "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" + "packages/oas-to-har/node_modules/@vitest/pretty-format": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", + "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "packages/oas-to-har/node_modules/@vitest/runner": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", + "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", + "dev": true, "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "@vitest/utils": "3.0.5", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "packages/oas-to-har/node_modules/@vitest/snapshot": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", + "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" + "@vitest/pretty-format": "3.0.5", + "magic-string": "^0.30.17", + "pathe": "^2.0.2" }, - "bin": { - "node-which": "bin/node-which" + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/oas-to-har/node_modules/@vitest/spy": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", + "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "packages/oas-to-har/node_modules/@vitest/utils": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", + "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", "dev": true, "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "@vitest/pretty-format": "3.0.5", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/vitest" } }, - "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "packages/oas-to-har/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/oas-to-har/node_modules/vite-node": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", + "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", "dev": true, "license": "MIT", "dependencies": { - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.2", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" }, "engines": { - "node": ">= 0.4" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/vitest" } }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "packages/oas-to-har/node_modules/vitest": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", + "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", "dev": true, "license": "MIT", "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" + "@vitest/expect": "3.0.5", + "@vitest/mocker": "3.0.5", + "@vitest/pretty-format": "^3.0.5", + "@vitest/runner": "3.0.5", + "@vitest/snapshot": "3.0.5", + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.5", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.5", + "@vitest/ui": "3.0.5", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "packages/oas-to-snippet": { + "name": "@readme/oas-to-snippet", + "version": "26.0.7", + "license": "MIT", + "dependencies": { + "@readme/httpsnippet": "^11.0.0", + "@readme/oas-to-har": "file:../oas-to-har" }, - "engines": { - "node": ">= 0.4" + "devDependencies": { + "@readme/oas-examples": "^5.12.0", + "@types/har-format": "^1.2.14", + "@types/node": "^22.7.6", + "har-examples": "^3.1.1", + "httpsnippet-client-api": "^7.0.0-beta.4", + "oas": "file:../oas", + "tsup": "^8.0.2", + "type-fest": "^4.18.3", + "typescript": "^5.2.2", + "vitest": "^3.0.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18" } }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "packages/oas-to-snippet/node_modules/@vitest/expect": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", + "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", "dev": true, "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "tinyrainbow": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/vitest" } }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "packages/oas-to-snippet/node_modules/@vitest/mocker": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", + "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", "dev": true, "license": "MIT", "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" + "@vitest/spy": "3.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" }, - "bin": { - "why-is-node-running": "cli.js" + "funding": { + "url": "https://opencollective.com/vitest" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "packages/oas-to-snippet/node_modules/@vitest/pretty-format": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", + "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "packages/oas-to-snippet/node_modules/@vitest/runner": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", + "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", "dev": true, "license": "MIT", "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" + "@vitest/utils": "3.0.5", + "pathe": "^2.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "node_modules/widest-line/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "packages/oas-to-snippet/node_modules/@vitest/snapshot": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", + "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@vitest/pretty-format": "3.0.5", + "magic-string": "^0.30.17", + "pathe": "^2.0.2" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://opencollective.com/vitest" } }, - "node_modules/widest-line/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "packages/oas-to-snippet/node_modules/@vitest/spy": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", + "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" + "tinyspy": "^3.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "node_modules/widest-line/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "packages/oas-to-snippet/node_modules/@vitest/utils": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", + "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" + "@vitest/pretty-format": "3.0.5", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://opencollective.com/vitest" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "packages/oas-to-snippet/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", "dev": true, - "license": "MIT", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "node": ">=16" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "packages/oas-to-snippet/node_modules/vite-node": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", + "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.2", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" }, "engines": { - "node": ">=10" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://opencollective.com/vitest" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "packages/oas-to-snippet/node_modules/vitest": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", + "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" + "@vitest/expect": "3.0.5", + "@vitest/mocker": "3.0.5", + "@vitest/pretty-format": "^3.0.5", + "@vitest/runner": "3.0.5", + "@vitest/snapshot": "3.0.5", + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.5", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.5", + "@vitest/ui": "3.0.5", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/write-json-file": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz", - "integrity": "sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==", + "packages/oas/node_modules/@vitest/expect": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", + "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", "dev": true, "license": "MIT", "dependencies": { - "detect-indent": "^5.0.0", - "graceful-fs": "^4.1.15", - "make-dir": "^2.1.0", - "pify": "^4.0.1", - "sort-keys": "^2.0.0", - "write-file-atomic": "^2.4.2" + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "tinyrainbow": "^2.0.0" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/write-json-file/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "packages/oas/node_modules/@vitest/mocker": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", + "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", "dev": true, "license": "MIT", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "@vitest/spy": "3.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/write-json-file/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "packages/oas/node_modules/@vitest/pretty-format": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", + "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/write-json-file/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/write-json-file/node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "license": "ISC", "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/write-pkg": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz", - "integrity": "sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==", + "packages/oas/node_modules/@vitest/runner": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", + "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", "dev": true, "license": "MIT", "dependencies": { - "sort-keys": "^2.0.0", - "type-fest": "^0.4.1", - "write-json-file": "^3.2.0" + "@vitest/utils": "3.0.5", + "pathe": "^2.0.2" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/write-pkg/node_modules/type-fest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", - "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=6" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "packages/oas/node_modules/@vitest/snapshot": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", + "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@vitest/pretty-format": "3.0.5", + "magic-string": "^0.30.17", + "pathe": "^2.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "packages/oas/node_modules/@vitest/spy": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", + "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" + "dependencies": { + "tinyspy": "^3.0.2" }, - "engines": { - "node": ">= 14" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "packages/oas/node_modules/@vitest/utils": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", + "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "dev": true, "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "@vitest/pretty-format": "3.0.5", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "packages/oas/node_modules/vite-node": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", + "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", "dev": true, "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.2", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, "engines": { - "node": ">=10" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "packages/oas/node_modules/vitest": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", + "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", "dev": true, "license": "MIT", + "dependencies": { + "@vitest/expect": "3.0.5", + "@vitest/mocker": "3.0.5", + "@vitest/pretty-format": "^3.0.5", + "@vitest/runner": "3.0.5", + "@vitest/snapshot": "3.0.5", + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.5", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.5", + "@vitest/ui": "3.0.5", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "packages/oas": { - "version": "25.3.0", + "packages/parser": { + "name": "@readme/openapi-parser", + "version": "2.7.0", "license": "MIT", "dependencies": { - "@readme/json-schema-ref-parser": "^1.2.0", - "@types/json-schema": "^7.0.11", - "json-schema-merge-allof": "^0.8.1", - "jsonpath-plus": "^10.0.0", - "jsonpointer": "^5.0.0", - "memoizee": "^0.4.16", - "openapi-types": "^12.1.1", - "path-to-regexp": "^8.1.0", - "remove-undefined-objects": "^6.0.0" + "@apidevtools/json-schema-ref-parser": "^11.9.0", + "@jsdevtools/ono": "^7.1.3", + "@readme/better-ajv-errors": "^2.1.2", + "@readme/openapi-schemas": "^3.1.0", + "ajv": "^8.12.0", + "ajv-draft-04": "^1.0.0", + "lodash": "^4.17.21" }, "devDependencies": { - "@readme/oas-examples": "^5.13.0", - "@readme/openapi-parser": "file:../parser", - "@types/json-schema-merge-allof": "^0.6.5", - "@types/memoizee": "^0.4.11", - "@types/node": "^22.7.6", - "tsup": "^8.0.2", - "typescript": "^5.4.4" + "@types/lodash": "^4.17.15", + "@types/node": "^22.13.1", + "eslint": "^8.56.0", + "openapi-types": "^12.1.3", + "tsup": "^8.3.6", + "typescript": "^5.7.3", + "vitest": "^3.0.5" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "openapi-types": ">=7" } }, - "packages/oas-normalize": { - "version": "12.1.0", + "packages/parser/node_modules/@vitest/expect": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", + "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", + "dev": true, "license": "MIT", "dependencies": { - "@readme/openapi-parser": "file:../parser", - "@readme/postman-to-openapi": "^4.1.0", - "js-yaml": "^4.1.0", - "openapi-types": "^12.1.3", - "swagger2openapi": "^7.0.8" - }, - "devDependencies": { - "@readme/oas-examples": "^5.12.0", - "@types/js-yaml": "^4.0.9", - "@types/swagger2openapi": "^7.0.4", - "eslint": "^8.57.0", - "nock": "^14.0.0", - "tsup": "^8.0.2", - "typescript": "^5.1.6" + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "tinyrainbow": "^2.0.0" }, - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "packages/oas-to-har": { - "name": "@readme/oas-to-har", - "version": "24.0.7", - "license": "ISC", + "packages/parser/node_modules/@vitest/mocker": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", + "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", + "dev": true, + "license": "MIT", "dependencies": { - "@readme/data-urls": "^3.0.0", - "oas": "file:../oas", - "qs": "^6.12.0", - "remove-undefined-objects": "^6.0.0" + "@vitest/spy": "3.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" }, - "devDependencies": { - "@readme/oas-examples": "^5.12.0", - "@types/har-format": "^1.2.15", - "@types/qs": "^6.9.14", - "eslint": "^8.57.0", - "jest-expect-har": "^7.1.0", - "tsup": "^8.0.2", - "type-fest": "^4.18.3", - "typescript": "^5.2.2" + "funding": { + "url": "https://opencollective.com/vitest" }, - "engines": { - "node": ">=18" + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "packages/oas-to-har/node_modules/type-fest": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", - "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "packages/parser/node_modules/@vitest/pretty-format": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", + "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "packages/oas-to-snippet": { - "name": "@readme/oas-to-snippet", - "version": "26.0.7", + "packages/parser/node_modules/@vitest/runner": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", + "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", + "dev": true, "license": "MIT", "dependencies": { - "@readme/httpsnippet": "^11.0.0", - "@readme/oas-to-har": "file:../oas-to-har" - }, - "devDependencies": { - "@readme/oas-examples": "^5.12.0", - "@types/har-format": "^1.2.14", - "@types/node": "^22.7.6", - "har-examples": "^3.1.1", - "httpsnippet-client-api": "^7.0.0-beta.4", - "oas": "file:../oas", - "tsup": "^8.0.2", - "type-fest": "^4.18.3", - "typescript": "^5.2.2" + "@vitest/utils": "3.0.5", + "pathe": "^2.0.2" }, - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "packages/oas-to-snippet/node_modules/type-fest": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", - "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "packages/parser/node_modules/@vitest/snapshot": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", + "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.5", + "magic-string": "^0.30.17", + "pathe": "^2.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "packages/parser": { - "name": "@readme/openapi-parser", - "version": "2.7.0", + "packages/parser/node_modules/@vitest/spy": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", + "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "dev": true, "license": "MIT", "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@readme/better-ajv-errors": "^2.1.2", - "@readme/json-schema-ref-parser": "^1.2.0", - "@readme/openapi-schemas": "^3.1.0", - "ajv": "^8.12.0", - "ajv-draft-04": "^1.0.0" - }, - "devDependencies": { - "@types/node": "^22.13.1", - "eslint": "^8.56.0", - "openapi-types": "^12.1.3", - "typescript": "^5.7.3" + "tinyspy": "^3.0.2" }, - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/parser/node_modules/@vitest/utils": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", + "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.5", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" }, - "peerDependencies": { - "openapi-types": ">=7" + "funding": { + "url": "https://opencollective.com/vitest" } }, "packages/parser/node_modules/ajv": { @@ -20908,6 +21333,99 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "packages/parser/node_modules/vite-node": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", + "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.2", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "packages/parser/node_modules/vitest": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", + "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.0.5", + "@vitest/mocker": "3.0.5", + "@vitest/pretty-format": "^3.0.5", + "@vitest/runner": "3.0.5", + "@vitest/snapshot": "3.0.5", + "@vitest/spy": "3.0.5", + "@vitest/utils": "3.0.5", + "chai": "^5.1.2", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.5", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.5", + "@vitest/ui": "3.0.5", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "packages/postman-types": { "version": "1.0.1", "license": "MIT", diff --git a/package.json b/package.json index cfb50e7ee..10512f4b8 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "./packages/*" ], "devDependencies": { - "@readme/eslint-config": "^14.0.0", + "@readme/eslint-config": "^14.2.0", "@vitest/coverage-v8": "^3.0.4", "alex": "^11.0.1", "eslint": "^8.57.0", diff --git a/packages/oas-normalize/package.json b/packages/oas-normalize/package.json index 3b7f7a0ca..49469eaf3 100644 --- a/packages/oas-normalize/package.json +++ b/packages/oas-normalize/package.json @@ -77,7 +77,8 @@ "eslint": "^8.57.0", "nock": "^14.0.0", "tsup": "^8.0.2", - "typescript": "^5.1.6" + "typescript": "^5.1.6", + "vitest": "^3.0.5" }, "prettier": "@readme/eslint-config/prettier" } diff --git a/packages/oas-normalize/src/index.ts b/packages/oas-normalize/src/index.ts index 1a2a861fb..57cabf26a 100644 --- a/packages/oas-normalize/src/index.ts +++ b/packages/oas-normalize/src/index.ts @@ -1,9 +1,10 @@ import type { Options } from './lib/types.js'; +import type { ParserOptions } from '@readme/openapi-parser'; import type { OpenAPI, OpenAPIV2, OpenAPIV3 } from 'openapi-types'; import fs from 'node:fs'; -import openapiParser from '@readme/openapi-parser'; +import { OpenAPIParser } from '@readme/openapi-parser'; import postmanToOpenAPI from '@readme/postman-to-openapi'; import converter from 'swagger2openapi'; @@ -108,9 +109,8 @@ export default class OASNormalize { return schema; }) - .then(schema => openapiParser.bundle(schema)) + .then(schema => new OpenAPIParser().bundle(schema)) .then(bundle => { - // @ts-expect-error The typings on the parser are messed up rigth now while a rewrite is in progress. this.cache.bundle = bundle; return bundle; }); @@ -134,9 +134,8 @@ export default class OASNormalize { return schema; }) - .then(schema => openapiParser.dereference(schema)) + .then(schema => new OpenAPIParser().dereference(schema)) .then(dereferenced => { - // @ts-expect-error The typings on the parser are messed up rigth now while a rewrite is in progress. this.cache.deref = dereferenced; return dereferenced; }); @@ -181,7 +180,7 @@ export default class OASNormalize { */ async validate( opts: { - parser?: openapiParser.Options; + parser?: ParserOptions; } = {}, ): Promise { const parserOptions = opts.parser || {}; @@ -208,7 +207,7 @@ export default class OASNormalize { } /** - * `openapiParser.validate()` dereferences schemas at the same time as validation, mutating + * `OpenAPIParser.validate()` dereferences schemas at the same time as validation, mutating * the supplied parameter in the process, and does not give us an option to disable this. * As we already have a dereferencing method on this library, and this method just needs to * tell us if the API definition is valid or not, we need to clone the schema before @@ -217,8 +216,7 @@ export default class OASNormalize { // eslint-disable-next-line try-catch-failsafe/json-parse const clonedSchema = JSON.parse(JSON.stringify(schema)); - // @ts-expect-error The typings on the parser are messed up rigth now while a rewrite is in progress. - return openapiParser.validate(clonedSchema, parserOptions).then(() => { + return new OpenAPIParser().validate(clonedSchema, parserOptions).then(() => { // The API definition, whatever its format or specification, is valid. return true; }); diff --git a/packages/oas-to-har/package.json b/packages/oas-to-har/package.json index 45fdf322e..e2f91962b 100644 --- a/packages/oas-to-har/package.json +++ b/packages/oas-to-har/package.json @@ -62,7 +62,8 @@ "jest-expect-har": "^7.1.0", "tsup": "^8.0.2", "type-fest": "^4.18.3", - "typescript": "^5.2.2" + "typescript": "^5.2.2", + "vitest": "^3.0.5" }, "prettier": "@readme/eslint-config/prettier" } diff --git a/packages/oas-to-snippet/package.json b/packages/oas-to-snippet/package.json index ad604ffda..58c12bcc4 100644 --- a/packages/oas-to-snippet/package.json +++ b/packages/oas-to-snippet/package.json @@ -62,7 +62,8 @@ "oas": "file:../oas", "tsup": "^8.0.2", "type-fest": "^4.18.3", - "typescript": "^5.2.2" + "typescript": "^5.2.2", + "vitest": "^3.0.5" }, "prettier": "@readme/eslint-config/prettier" } diff --git a/packages/oas/package.json b/packages/oas/package.json index bd639e23c..6d7337c53 100644 --- a/packages/oas/package.json +++ b/packages/oas/package.json @@ -105,7 +105,8 @@ "@types/memoizee": "^0.4.11", "@types/node": "^22.7.6", "tsup": "^8.0.2", - "typescript": "^5.4.4" + "typescript": "^5.4.4", + "vitest": "^3.0.5" }, "prettier": "@readme/eslint-config/prettier" } diff --git a/packages/oas/test/operation/index.test.ts b/packages/oas/test/operation/index.test.ts index 470946347..a8b2903ae 100644 --- a/packages/oas/test/operation/index.test.ts +++ b/packages/oas/test/operation/index.test.ts @@ -1,7 +1,7 @@ import type * as RMOAS from '../../src/types.js'; import petstoreSpec from '@readme/oas-examples/3.0/json/petstore.json'; -import openapiParser from '@readme/openapi-parser'; +import { OpenAPIParser } from '@readme/openapi-parser'; import { beforeAll, describe, it, expect } from 'vitest'; import Oas from '../../src/index.js'; @@ -708,7 +708,7 @@ describe('#getSecurityWithTypes()', () => { // The original API doc should still be valid. const clonedSpec = JSON.parse(JSON.stringify(spec.api)); - await expect(openapiParser.validate(clonedSpec)).resolves.toStrictEqual( + await expect(OpenAPIParser.validate(clonedSpec)).resolves.toStrictEqual( expect.objectContaining({ openapi: '3.1.0', }), diff --git a/packages/oas/test/operation/lib/get-response-as-json-schema.test.ts b/packages/oas/test/operation/lib/get-response-as-json-schema.test.ts index 145487034..04b751f45 100644 --- a/packages/oas/test/operation/lib/get-response-as-json-schema.test.ts +++ b/packages/oas/test/operation/lib/get-response-as-json-schema.test.ts @@ -1,6 +1,6 @@ import type { HttpMethods, ResponseObject, SchemaObject } from '../../../src/types.js'; -import openapiParser from '@readme/openapi-parser'; +import { OpenAPIParser } from '@readme/openapi-parser'; import { beforeAll, describe, test, expect, it } from 'vitest'; import Oas from '../../../src/index.js'; @@ -301,7 +301,7 @@ describe('quirks', () => { }); // The original spec should still validate too! - await expect(openapiParser.validate(cloneObject(definition))).resolves.toStrictEqual( + await expect(OpenAPIParser.validate(cloneObject(definition))).resolves.toStrictEqual( expect.objectContaining({ openapi: '3.0.2', }), diff --git a/packages/parser/.editorconfig b/packages/parser/.editorconfig deleted file mode 100644 index 7f37c99b2..000000000 --- a/packages/parser/.editorconfig +++ /dev/null @@ -1,27 +0,0 @@ -# Editor config -# http://EditorConfig.org - -# This EditorConfig overrides any parent EditorConfigs -root = true - -# Default rules applied to all file types -[*] - -# No trailing spaces, newline at EOF -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -end_of_line = lf - -# 2 space indentation -indent_style = space -indent_size = 2 - -# JavaScript-specific settings -[*.{js,ts}] -quote_type = double -continuation_indent_size = 2 -curly_bracket_next_line = false -indent_brace_style = BSD -spaces_around_operators = true -spaces_around_brackets = none diff --git a/packages/parser/.eslintrc b/packages/parser/.eslintrc index 2ed1b63db..585e54621 100644 --- a/packages/parser/.eslintrc +++ b/packages/parser/.eslintrc @@ -1,49 +1,10 @@ { - "root": true, - "extends": [ - "@readme/eslint-config", - "@readme/eslint-config/typescript", - ], - "env": { - "browser": true, - "node": true - }, "rules": { + "camelcase": ["error", { "allow": ["OpenAPIV3_1"] }], + "lines-between-class-members": "off", + "no-continue": "off", "no-plusplus": "off", - "no-restricted-syntax": "off", - "no-underscore-dangle": "off", - "no-use-before-define": "off", "prefer-rest-params": "off", - "prefer-spread": "off" + "prefer-spread": "off", }, - "overrides": [ - { - // The typings in this file are pretty bad right now, when we have native types we can - // remove this. - "files": ["lib/index.d.ts"], - "rules": { - "@typescript-eslint/consistent-indexed-object-style": "off", - "@typescript-eslint/consistent-type-imports": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/sort-type-constituents": "off", - "eslint-comments/no-unused-disable": "off", - "lines-between-class-members": "off", - "max-classes-per-file": "off", - "quotes": "off", - "typescript-sort-keys/interface": "off" - } - }, - { - // These can all get removed when the library is moved over to native TS. - "files": ["*.js"], - "rules": { - "@typescript-eslint/no-this-alias": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-use-before-define": "off", - "@typescript-eslint/no-var-requires": "off", - "eslint-comments/no-unused-disable": "off", - "func-names": "off" - } - } - ] } diff --git a/packages/parser/.gitattributes b/packages/parser/.gitattributes deleted file mode 100644 index 64c7f58c1..000000000 --- a/packages/parser/.gitattributes +++ /dev/null @@ -1,26 +0,0 @@ -# Git attributes -# https://git-scm.com/docs/gitattributes -# https://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes - -# Normalize line endings for all files that git determines to be text. -# https://git-scm.com/docs/gitattributes#gitattributes-Settostringvalueauto -* text=auto - -# Normalize line endings to LF on checkin, and do NOT convert to CRLF when checking-out on Windows. -# https://git-scm.com/docs/gitattributes#gitattributes-Settostringvaluelf -*.txt text eol=lf -*.html text eol=lf -*.md text eol=lf -*.css text eol=lf -*.scss text eol=lf -*.map text eol=lf -*.js text eol=lf -*.jsx text eol=lf -*.ts text eol=lf -*.mts text eol=lf -*.tsx text eol=lf -*.json text eol=lf -*.yml text eol=lf -*.yaml text eol=lf -*.xml text eol=lf -*.svg text eol=lf diff --git a/packages/parser/.github/dependabot.yml b/packages/parser/.github/dependabot.yml deleted file mode 100644 index 77defa8e9..000000000 --- a/packages/parser/.github/dependabot.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: 2 -updates: - - package-ecosystem: github-actions - directory: '/' - schedule: - interval: monthly - reviewers: - - erunion - labels: - - dependencies - commit-message: - prefix: chore(deps) - prefix-development: chore(deps-dev) - - - package-ecosystem: npm - directory: '/' - schedule: - interval: monthly - open-pull-requests-limit: 10 - reviewers: - - erunion - labels: - - dependencies - groups: - minor-development-deps: - dependency-type: 'development' - update-types: - - minor - - patch - commit-message: - prefix: chore(deps) - prefix-development: chore(deps-dev) diff --git a/packages/parser/.github/workflows/ci.yml b/packages/parser/.github/workflows/ci.yml deleted file mode 100644 index f9aa11857..000000000 --- a/packages/parser/.github/workflows/ci.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: CI - -on: - push: - branches: - - main - pull_request: - -jobs: - node_tests: - name: Node ${{ matrix.node }} on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - timeout-minutes: 10 - strategy: - fail-fast: true - matrix: - os: - - ubuntu-latest - - windows-latest - node: - - lts/-1 - - lts/* - - latest - - steps: - - name: Checkout source - uses: actions/checkout@v4 - - - name: Install Node ${{ matrix.node }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node }} - cache: npm - - - run: npm ci - - run: npm run lint - - - name: Run tests - run: npm run test --ignore-scripts diff --git a/packages/parser/.github/workflows/lint-pr-title.yml b/packages/parser/.github/workflows/lint-pr-title.yml deleted file mode 100644 index c227e2b33..000000000 --- a/packages/parser/.github/workflows/lint-pr-title.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: 'Lint PR Title' - -on: - pull_request_target: - types: - - opened - - edited - - synchronize - -permissions: - pull-requests: read - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/packages/parser/.gitignore b/packages/parser/.gitignore deleted file mode 100644 index b6a5989b6..000000000 --- a/packages/parser/.gitignore +++ /dev/null @@ -1,44 +0,0 @@ -# Git ignore -# https://git-scm.com/docs/gitignore - -# Miscellaneous -*~ -*# -.DS_STORE -Thumbs.db -.netbeans -nbproject -.node_history - -# IDEs & Text Editors -.idea -.sublime-* -.netbeans -nbproject - -# Temporary files -.tmp -.temp -.grunt -.lock-wscript - -# Logs -/logs -*.log - -# Runtime data -pids -*.pid -*.seed - -# Dependencies -node_modules - -# Test output -/.nyc_output -/coverage -debug.js - -# Jekyll output -_site -.sass-cache diff --git a/packages/parser/.npmignore b/packages/parser/.npmignore deleted file mode 100644 index a34d18efa..000000000 --- a/packages/parser/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -.github/ -coverage/ -.eslint* -vitest.* diff --git a/packages/parser/.npmrc b/packages/parser/.npmrc deleted file mode 100644 index 4eae7876f..000000000 --- a/packages/parser/.npmrc +++ /dev/null @@ -1 +0,0 @@ -lockfile-version=3 diff --git a/packages/parser/.prettierignore b/packages/parser/.prettierignore deleted file mode 100644 index f9cae01d0..000000000 --- a/packages/parser/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -test/specs/large-file-memory-leak/cloudflare-stringified.json diff --git a/packages/parser/.vscode/settings.json b/packages/parser/.vscode/settings.json deleted file mode 100644 index c8e04def6..000000000 --- a/packages/parser/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "editor.codeActionsOnSave": { - "source.fixAll": "explicit" - } -} diff --git a/packages/parser/bin/refresh-real-world-apis b/packages/parser/bin/refresh-real-world-apis index 025d47129..74767d0a6 100755 --- a/packages/parser/bin/refresh-real-world-apis +++ b/packages/parser/bin/refresh-real-world-apis @@ -1,13 +1,14 @@ #! /usr/bin/env node -/* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable no-param-reassign */ /** - * This script regenerates the list of real world API definitions from APIs.guru that the test suite uses for parsing - * and validating integration tests. + * This script regenerates the list of real world API definitions from APIs.guru that the test + * suite uses for parsing and validating integration tests. * - * @link https://github.com/APIs-guru/openapi-directory + * @see {@link https://github.com/APIs-guru/openapi-directory} */ -const fs = require('fs'); +import fs from 'node:fs'; + +const __dirname = import.meta.dirname; fetch('https://api.apis.guru/v2/list.json') .then(response => { diff --git a/packages/parser/lib/index.d.ts b/packages/parser/lib/index.d.ts deleted file mode 100644 index 03febcbf7..000000000 --- a/packages/parser/lib/index.d.ts +++ /dev/null @@ -1,460 +0,0 @@ -import { OpenAPI } from 'openapi-types'; - -export = OpenAPIParser; - -/** - * This is the default export of Swagger Parser. You can creates instances of this class using new OpenAPIParser(), or you can just call its static methods. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md - */ -declare class OpenAPIParser { - /** - * The `api` property is the parsed/bundled/dereferenced OpenAPI definition. This is the same value that is returned when calling the parse, bundle, or dereference methods. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#api - */ - public api: OpenAPI.Document; - - /** - * The $refs property is a `$Refs` object, which lets you access all of the externally-referenced files in the OpenAPI definition, as well as easily get and set specific values in the OpenAPI definition using JSON pointers. - * - * This is the same value that is returned when calling the `resolve` method. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#refs - */ - public $refs: OpenAPIParser.$Refs; - - /** - * Parses, dereferences, and validates the given Swagger API. - * Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#validateapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. - * @param options (optional) - */ - public validate(api: string | OpenAPI.Document): void; - public validate(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public validate(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public validate(api: string | OpenAPI.Document): Promise; - public validate(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public validate( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * Parses, dereferences, and validates the given Swagger API. - * Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#validateapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. - * @param options (optional) - */ - public static validate(api: string | OpenAPI.Document): void; - public static validate(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static validate(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static validate(api: string | OpenAPI.Document): Promise; - public static validate(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public static validate( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * Dereferences all `$ref` pointers in the OpenAPI definition, replacing each reference with its resolved value. This results in an API definition that does not contain any `$ref` pointers. Instead, it's a normal JavaScript object tree that can easily be crawled and used just like any other JavaScript object. This is great for programmatic usage, especially when using tools that don't understand JSON references. - * - * The dereference method maintains object reference equality, meaning that all `$ref` pointers that point to the same object will be replaced with references to the same object. Again, this is great for programmatic usage, but it does introduce the risk of circular references, so be careful if you intend to serialize the API definition using `JSON.stringify()`. Consider using the bundle method instead, which does not create circular references. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#dereferenceapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. - * @param options (optional) - */ - public dereference(api: string | OpenAPI.Document): void; - public dereference(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public dereference(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public dereference(api: string | OpenAPI.Document): Promise; - public dereference(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public dereference( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * Dereferences all `$ref` pointers in the OpenAPI definition, replacing each reference with its resolved value. This results in an API definition that does not contain any `$ref` pointers. Instead, it's a normal JavaScript object tree that can easily be crawled and used just like any other JavaScript object. This is great for programmatic usage, especially when using tools that don't understand JSON references. - * - * The dereference method maintains object reference equality, meaning that all `$ref` pointers that point to the same object will be replaced with references to the same object. Again, this is great for programmatic usage, but it does introduce the risk of circular references, so be careful if you intend to serialize the API definition using `JSON.stringify()`. Consider using the bundle method instead, which does not create circular references. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#dereferenceapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. - * @param options (optional) - */ - public static dereference(api: string | OpenAPI.Document): void; - public static dereference(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static dereference(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static dereference(api: string | OpenAPI.Document): Promise; - public static dereference(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public static dereference( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * Bundles all referenced files/URLs into a single API definition that only has internal `$ref` pointers. This lets you split-up your API definition however you want while you're building it, but easily combine all those files together when it's time to package or distribute the API definition to other people. The resulting API definition size will be small, since it will still contain internal JSON references rather than being fully-dereferenced. - * - * This also eliminates the risk of circular references, so the API definition can be safely serialized using `JSON.stringify()`. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#bundleapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. - * @param options (optional) - */ - public bundle(api: string | OpenAPI.Document): void; - public bundle(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public bundle(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public bundle(api: string | OpenAPI.Document): Promise; - public bundle(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public bundle( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * Bundles all referenced files/URLs into a single API definition that only has internal `$ref` pointers. This lets you split-up your API definition however you want while you're building it, but easily combine all those files together when it's time to package or distribute the API definition to other people. The resulting API definition size will be small, since it will still contain internal JSON references rather than being fully-dereferenced. - * - * This also eliminates the risk of circular references, so the API definition can be safely serialized using `JSON.stringify()`. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#bundleapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. - * @param options (optional) - */ - public static bundle(api: string | OpenAPI.Document): void; - public static bundle(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static bundle(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static bundle(api: string | OpenAPI.Document): Promise; - public static bundle(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public static bundle( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * *This method is used internally by other methods, such as `bundle` and `dereference`. You probably won't need to call this method yourself.* - * - * Parses the given OpenAPI definition file (in JSON or YAML format), and returns it as a JavaScript object. This method `does not` resolve `$ref` pointers or dereference anything. It simply parses one file and returns it. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#parseapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. The path can be absolute or relative. In Node, the path is relative to `process.cwd()`. In the browser, it's relative to the URL of the page. - * @param options (optional) - */ - public parse(api: string | OpenAPI.Document): void; - public parse(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public parse(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public parse(api: string | OpenAPI.Document): Promise; - public parse(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public parse( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * *This method is used internally by other methods, such as `bundle` and `dereference`. You probably won't need to call this method yourself.* - * - * Parses the given OpenAPI definition file (in JSON or YAML format), and returns it as a JavaScript object. This method `does not` resolve `$ref` pointers or dereference anything. It simply parses one file and returns it. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#parseapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. The path can be absolute or relative. In Node, the path is relative to `process.cwd()`. In the browser, it's relative to the URL of the page. - * @param options (optional) - */ - public static parse(api: string | OpenAPI.Document): void; - public static parse(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static parse(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static parse(api: string | OpenAPI.Document): Promise; - public static parse(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public static parse( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * *This method is used internally by other methods, such as `bundle` and `dereference`. You probably won't need to call this method yourself.* - * - * Resolves all JSON references (`$ref` pointers) in the given OpenAPI definition file. If it references any other files/URLs, then they will be downloaded and resolved as well. This method **does not** dereference anything. It simply gives you a `$Refs` object, which is a map of all the resolved references and their values. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#resolveapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. - * @param options (optional) - */ - public resolve(api: string | OpenAPI.Document): void; - public resolve(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public resolve(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public resolve(api: string | OpenAPI.Document): Promise; - public resolve(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public resolve( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; - - /** - * *This method is used internally by other methods, such as `bundle` and `dereference`. You probably won't need to call this method yourself.* - * - * Resolves all JSON references (`$ref` pointers) in the given OpenAPI definition file. If it references any other files/URLs, then they will be downloaded and resolved as well. This method **does not** dereference anything. It simply gives you a `$Refs` object, which is a map of all the resolved references and their values. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#resolveapi-options - * - * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. - * @param options (optional) - */ - public static resolve(api: string | OpenAPI.Document): void; - public static resolve(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static resolve(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; - public static resolve(api: string | OpenAPI.Document): Promise; - public static resolve(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; - public static resolve( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - ): Promise; -} - -// eslint-disable-next-line no-redeclare -declare namespace OpenAPIParser { - /** - * See https://github.com/readmeio/openapi-parser/blob/main/docs/options.md - */ - export interface Options { - /** - * The `parse` options determine how different types of files will be parsed. - * - * JSON Schema `$Ref` Parser comes with built-in JSON, YAML, plain-text, and binary parsers, any of which you can configure or disable. You can also add your own custom parsers if you want. - */ - parse?: { - json?: ParserOptions | boolean; - yaml?: ParserOptions | boolean; - text?: (ParserOptions & { encoding?: string }) | boolean; - [key: string]: ParserOptions | boolean | undefined; - }; - - /** - * The `resolve` options control how Swagger Parser will resolve file paths and URLs, and how those files will be read/downloaded. - * - * JSON Schema `$Ref` Parser comes with built-in support for HTTP and HTTPS, as well as support for local files (when running in Node.js). You can configure or disable either of these built-in resolvers. You can also add your own custom resolvers if you want. - */ - resolve?: { - /** - * Determines whether external $ref pointers will be resolved. If this option is disabled, then external `$ref` pointers will simply be ignored. - */ - external?: boolean; - file?: Partial | boolean; - http?: HTTPResolverOptions | boolean; - }; - - /** - * The `dereference` options control how JSON Schema `$Ref` Parser will dereference `$ref` pointers within the JSON schema. - */ - dereference?: { - /** - * Determines whether circular `$ref` pointers are handled. - * - * If set to `false`, then a `ReferenceError` will be thrown if the schema contains any circular references. - * - * If set to `"ignore"`, then circular references will simply be ignored. No error will be thrown, but the `$Refs.circular` property will still be set to `true`. - */ - circular?: boolean | 'ignore'; - }; - - /** - * The `validate` options control how Swagger Parser will validate the API. - */ - validate?: { - /** - * If set to `true` then validation errors will be colorized and styled. - */ - colorizeErrors?: boolean | false; - - /** - * If set to `false`, then validating against the Swagger 2.0 Schema, OpenAPI 3.0 Schema, or OpenAPI 3.1 Schema is disabled. - */ - schema?: boolean; - - /** - * If set to `false`, then validating against the Swagger 2.0 Specification is disabled. - */ - spec?: boolean; - }; - } - - export interface HTTPResolverOptions extends Partial { - /** - * You can specify any HTTP headers that should be sent when downloading files. For example, some servers may require you to set the `Accept` or `Referrer` header. - */ - headers?: object; - - /** - * The amount of time (in milliseconds) to wait for a response from the server when downloading files. The default is 5 seconds. - */ - timeout?: number; - - /** - * The maximum number of HTTP redirects to follow per file. The default is 5. To disable automatic following of redirects, set this to zero. - */ - redirects?: number; - - /** - * Set this to `true` if you're downloading files from a CORS-enabled server that requires authentication - */ - withCredentials?: boolean; - } - - /** - * JSON Schema `$Ref` Parser comes with built-in resolvers for HTTP and HTTPS URLs, as well as local filesystem paths (when running in Node.js). You can add your own custom resolvers to support additional protocols, or even replace any of the built-in resolvers with your own custom implementation. - * - * See https://apitools.dev/json-schema-ref-parser/docs/plugins/resolvers.html - */ - export interface ResolverOptions { - /** - * All resolvers have an order property, even the built-in resolvers. If you don't specify an order property, then your resolver will run last. Specifying `order: 1`, like we did in this example, will make your resolver run first. Or you can squeeze your resolver in-between some of the built-in resolvers. For example, `order: 101` would make it run after the file resolver, but before the HTTP resolver. You can see the order of all the built-in resolvers by looking at their source code. - * - * The order property and canRead property are related to each other. For each file that Swagger Parser needs to resolve, it first determines which resolvers can read that file by checking their canRead property. If only one resolver matches a file, then only that one resolver is called, regardless of its order. If multiple resolvers match a file, then those resolvers are tried in order until one of them successfully reads the file. Once a resolver successfully reads the file, the rest of the resolvers are skipped. - */ - order?: number; - - /** - * The `canRead` property tells JSON Schema `$Ref` Parser what kind of files your resolver can read. In this example, we've simply specified a regular expression that matches "mogodb://" URLs, but we could have used a simple boolean, or even a function with custom logic to determine which files to resolve. Here are examples of each approach: - */ - canRead: boolean | RegExp | string | string[] | ((file: FileInfo) => boolean); - - /** - * This is where the real work of a resolver happens. The `read` method accepts the same file info object as the `canRead` function, but rather than returning a boolean value, the `read` method should return the contents of the file. The file contents should be returned in as raw a form as possible, such as a string or a byte array. Any further parsing or processing should be done by parsers. - * - * Unlike the `canRead` function, the `read` method can also be asynchronous. This might be important if your resolver needs to read data from a database or some other external source. You can return your asynchronous value using either an ES6 Promise or a Node.js-style error-first callback. Of course, if your resolver has the ability to return its data synchronously, then that's fine too. Here are examples of all three approaches: - */ - read( - file: FileInfo, - callback?: (error: Error | null, data: string | null) => any, - ): string | Buffer | Promise; - } - - export interface ParserOptions { - /** - * Parsers run in a specific order, relative to other parsers. For example, a parser with `order: 5` will run before a parser with `order: 10`. If a parser is unable to successfully parse a file, then the next parser is tried, until one succeeds or they all fail. - * - * You can change the order in which parsers run, which is useful if you know that most of your referenced files will be a certain type, or if you add your own custom parser that you want to run first. - */ - order?: number; - - /** - * All of the built-in parsers allow empty files by default. The JSON and YAML parsers will parse empty files as `undefined`. The text parser will parse empty files as an empty string. The binary parser will parse empty files as an empty byte array. - * - * You can set `allowEmpty: false` on any parser, which will cause an error to be thrown if a file empty. - */ - allowEmpty?: boolean; - - /** - * Determines which parsers will be used for which files. - * - * A regular expression can be used to match files by their full path. A string (or array of strings) can be used to match files by their file extension. Or a function can be used to perform more complex matching logic. See the custom parser docs for details. - */ - canParse?: boolean | RegExp | string | string[] | ((file: FileInfo) => boolean); - } - - /** - * JSON Schema `$Ref` Parser supports plug-ins, such as resolvers and parsers. These plug-ins can have methods such as `canRead()`, `read()`, `canParse()`, and `parse()`. All of these methods accept the same object as their parameter: an object containing information about the file being read or parsed. - * - * The file info object currently only consists of a few properties, but it may grow in the future if plug-ins end up needing more information. - * - * See https://apitools.dev/json-schema-ref-parser/docs/plugins/file-info-object.html - */ - export interface FileInfo { - /** - * The full URL of the file. This could be any type of URL, including "http://", "https://", "file://", "ftp://", "mongodb://", or even a local filesystem path (when running in Node.js). - */ - url: string; - - /** - * The lowercase file extension, such as ".json", ".yaml", ".txt", etc. - */ - extension: string; - - /** - * The raw file contents, in whatever form they were returned by the resolver that read the file. - */ - data: string | Buffer; - } - - /** - * When you call the resolve method, the value that is returned is a $Refs object. This same object is accessible via the parser.$refs property of OpenAPIParser objects. - * - * This object is a map of JSON References and their resolved values. It also has several convenient helper methods that make it easy for you to navigate and manipulate the JSON References. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md - */ - export class $Refs { - /** - * This property is true if the API definition contains any circular references. You may want to check this property before serializing the dereferenced API definition as JSON, since JSON.stringify() does not support circular references by default. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#circular - */ - public circular: boolean; - - /** - * Returns the paths/URLs of all the files in your API definition (including the main API definition file). - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#pathstypes - * - * @param types (optional) Optionally only return certain types of paths ("file", "http", etc.) - */ - public paths(...types: string[]): string[]; - - /** - * Returns a map of paths/URLs and their correspond values. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#valuestypes - * - * @param types (optional) Optionally only return values from certain locations ("file", "http", etc.) - */ - public values(...types: string[]): { [url: string]: any }; - - /** - * Returns `true` if the given path exists in the OpenAPI definition; otherwise, returns `false` - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#existsref - * - * @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash - */ - public exists($ref: string): boolean; - - /** - * Gets the value at the given path in the OpenAPI definition. Throws an error if the path does not exist. - * - * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#getref - * - * @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash - */ - public get($ref: string): any; - - /** - * Sets the value at the given path in the OpenAPI definition. If the property, or any of its parents, don't exist, they will be created. - * - * @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash - * @param value The value to assign. Can be anything (object, string, number, etc.) - */ - public set($ref: string, value: any): void; - } -} diff --git a/packages/parser/lib/index.js b/packages/parser/lib/index.js deleted file mode 100644 index 8b95bba45..000000000 --- a/packages/parser/lib/index.js +++ /dev/null @@ -1,180 +0,0 @@ -/* eslint-disable no-unused-vars */ -const { ono } = require('@jsdevtools/ono'); -const $RefParser = require('@readme/json-schema-ref-parser'); -const dereference = require('@readme/json-schema-ref-parser/lib/dereference'); -const normalizeArgs = require('@readme/json-schema-ref-parser/lib/normalize-args'); - -const Options = require('./options'); -const util = require('./util'); -const validateSchema = require('./validators/schema'); -const validateSpec = require('./validators/spec'); - -const supported31Versions = ['3.1.0', '3.1.1']; -const supported30Versions = ['3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.0.4']; -const supportedVersions = [...supported31Versions, ...supported30Versions]; - -module.exports = OpenAPIParser; - -/** - * This class parses a Swagger 2.0 or 3.0 API, resolves its JSON references and their resolved values, - * and provides methods for traversing, dereferencing, and validating the API. - * - * @class - * @augments $RefParser - */ -function OpenAPIParser() { - $RefParser.apply(this, arguments); -} - -util.inherits(OpenAPIParser, $RefParser); -OpenAPIParser.parse = $RefParser.parse; -OpenAPIParser.resolve = $RefParser.resolve; -OpenAPIParser.bundle = $RefParser.bundle; -OpenAPIParser.dereference = $RefParser.dereference; - -/** - * Alias {@link $RefParser#schema} as {@link OpenAPIParser#api} - */ -Object.defineProperty(OpenAPIParser.prototype, 'api', { - configurable: true, - enumerable: true, - get() { - return this.schema; - }, -}); - -/** - * Parses the given Swagger API. - * This method does not resolve any JSON references. - * It just reads a single file in JSON or YAML format, and parse it as a JavaScript object. - * - * @param {string} [path] - The file path or URL of the JSON schema - * @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`. - * @param {ParserOptions} [options] - Options that determine how the API is parsed - * @returns {Promise} - The returned promise resolves with the parsed API object. - */ -OpenAPIParser.prototype.parse = async function (path, api, options) { - const args = normalizeArgs(arguments); - args.options = new Options(args.options); - - const schema = await $RefParser.prototype.parse.call(this, args.path, args.schema, args.options); - - if (schema.swagger) { - // Verify that the parsed object is a Swagger API - if (schema.swagger === undefined || schema.info === undefined || schema.paths === undefined) { - throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid Swagger API definition.`); - } else if (typeof schema.swagger === 'number') { - // This is a very common mistake, so give a helpful error message - throw ono.syntax('Swagger version number must be a string (e.g. "2.0") not a number.'); - } else if (typeof schema.info.version === 'number') { - // This is a very common mistake, so give a helpful error message - throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.'); - } else if (schema.swagger !== '2.0') { - throw ono.syntax(`Unrecognized Swagger version: ${schema.swagger}. Expected 2.0`); - } - } else { - // Verify that the parsed object is an OpenAPI definition - if (schema.openapi === undefined || schema.info === undefined) { - throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); - } else if (schema.paths === undefined) { - // An OpenAPI 3.1 definition must have either `paths` or `webhooks`. If it has neither then - // it's invalid. - if (supported31Versions.includes(schema.openapi)) { - if (schema.webhooks === undefined) { - throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); - } - } else { - throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); - } - } else if (typeof schema.openapi === 'number') { - // This is a very common mistake, so give a helpful error message - throw ono.syntax('OpenAPI version number must be a string (e.g. "3.0.0") not a number.'); - } else if (typeof schema.info.version === 'number') { - // This is a very common mistake, so give a helpful error message - throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.'); - } else if (!supportedVersions.includes(schema.openapi)) { - throw ono.syntax( - `Unsupported OpenAPI version: ${schema.openapi}. This library only supports versions ${supportedVersions.join(', ')}`, - ); - } - - // This is an OpenAPI v3 schema, check if the "servers" have any relative paths and - // fix them if the content was pulled from a web resource - util.fixOasRelativeServers(schema, args.path); - } - - return schema; -}; - -/** - * Parses, dereferences, and validates the given Swagger API. - * Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation. - * - * @param {string} [path] - The file path or URL of the JSON schema - * @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`. - * @param {ParserOptions} [options] - Options that determine how the API is parsed, dereferenced, and validated - * @returns {Promise} - The returned promise resolves with the parsed API object. - */ -OpenAPIParser.validate = function (path, api, options) { - const Class = this; - const instance = new Class(); - return instance.validate.apply(instance, arguments); -}; - -/** - * Parses, dereferences, and validates the given Swagger API. - * Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation. - * - * @param {string} [path] - The file path or URL of the JSON schema - * @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`. - * @param {ParserOptions} [options] - Options that determine how the API is parsed, dereferenced, and validated - * @returns {Promise} - The returned promise resolves with the parsed API object. - */ -OpenAPIParser.prototype.validate = async function (path, api, options) { - const me = this; - const args = normalizeArgs(arguments); - args.options = new Options(args.options); - - // ZSchema doesn't support circular objects, so don't dereference circular $refs yet - // (see https://github.com/zaggino/z-schema/issues/137) - const circular$RefOption = args.options.dereference.circular; - if (args.options.validate.schema) { - args.options.dereference.circular = 'ignore'; - } - - await this.dereference(args.path, args.schema, args.options); - - // Restore the original options, now that we're done dereferencing - args.options.dereference.circular = circular$RefOption; - - if (args.options.validate.schema) { - // Validate the API against the Swagger schema - // NOTE: This is safe to do, because we haven't dereferenced circular $refs yet - validateSchema(me.api, args.options); - - if (me.$refs.circular) { - if (circular$RefOption === true) { - // The API has circular references, - // so we need to do a second-pass to fully-dereference it - dereference(me, args.options); - } else if (circular$RefOption === false) { - // The API has circular references, and they're not allowed, so throw an error - throw ono.reference('The API contains circular references'); - } - } - } - - if (args.options.validate.spec) { - // Validate the API against the Swagger spec - validateSpec(me.api); - } - - return me.schema; -}; - -/** - * The Swagger object - * https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object - * - * @typedef {{swagger: string, info: {}, paths: {}}} SwaggerObject - */ diff --git a/packages/parser/lib/options.js b/packages/parser/lib/options.js deleted file mode 100644 index f4363d527..000000000 --- a/packages/parser/lib/options.js +++ /dev/null @@ -1,36 +0,0 @@ -const $RefParserOptions = require('@readme/json-schema-ref-parser/lib/options'); - -const util = require('./util'); -const schemaValidator = require('./validators/schema'); -const specValidator = require('./validators/spec'); - -module.exports = ParserOptions; - -/** - * Options that determine how Swagger APIs are parsed, resolved, dereferenced, and validated. - * - * @param {object|ParserOptions} [_options] - Overridden options - * @class - * @augments $RefParserOptions - */ -// eslint-disable-next-line no-unused-vars -function ParserOptions(_options) { - $RefParserOptions.call(this, ParserOptions.defaults); - $RefParserOptions.apply(this, arguments); -} - -ParserOptions.defaults = { - /** - * Determines how the API definition will be validated. - * - * You can add additional validators of your own, replace an existing one with - * your own implemenation, or disable any validator by setting it to false. - */ - validate: { - colorizeErrors: false, - schema: schemaValidator, - spec: specValidator, - }, -}; - -util.inherits(ParserOptions, $RefParserOptions); diff --git a/packages/parser/lib/util.js b/packages/parser/lib/util.js deleted file mode 100644 index 982729be4..000000000 --- a/packages/parser/lib/util.js +++ /dev/null @@ -1,88 +0,0 @@ -// eslint-disable-next-line unicorn/import-style -const util = require('util'); - -const url = require('@readme/json-schema-ref-parser/lib/util/url'); - -exports.inherits = util.inherits; - -/** - * Regular Expression that matches Swagger path params. - */ -exports.swaggerParamRegExp = /\{([^/}]+)}/g; - -/** - * List of HTTP verbs used for OperationItem as per the Swagger specification - */ -const operationsList = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'trace']; - -/** - * This function takes in a Server object, checks if it has relative path - * and then fixes it as per the path url - * - * @param {object} server - The server object to be fixed - * @param {string} path - The path (an http/https url) from where the file was downloaded - * @returns {object} - The fixed server object - */ -function fixServers(server, path) { - // Server url starting with "/" tells that it is not an http(s) url - if (server.url && server.url.startsWith('/')) { - const inUrl = url.parse(path); - const finalUrl = `${inUrl.protocol}//${inUrl.hostname}${server.url}`; - server.url = finalUrl; // eslint-disable-line no-param-reassign - } - - return server; -} - -/** - * This function helps fix the relative servers in the API definition file - * be at root, path or operation's level - */ -function fixOasRelativeServers(schema, filePath) { - if (schema.openapi && filePath && (filePath.startsWith('http:') || filePath.startsWith('https:'))) { - /** - * From OpenAPI v3 spec for Server object's url property: "REQUIRED. A URL to the target host. - * This URL supports Server Variables and MAY be relative, to indicate that the host location is relative to the location where - * the OpenAPI document is being served." - * Further, the spec says that "servers" property can show up at root level, in 'Path Item' object or in 'Operation' object. - * However, interpretation of the spec says that relative paths for servers should take into account the hostname that - * serves the OpenAPI file. - */ - if (schema.servers) { - schema.servers.map(server => fixServers(server, filePath)); // Root level servers array's fixup - } - - // Path, Operation, or Webhook level servers array's fixup - ['paths', 'webhooks'].forEach(component => { - Object.keys(schema[component] || []).forEach(path => { - const pathItem = schema[component][path]; - Object.keys(pathItem).forEach(opItem => { - if (opItem === 'servers') { - // servers at pathitem level - pathItem[opItem].map(server => fixServers(server, filePath)); - } else if (operationsList.includes(opItem)) { - // servers at operation level - if (pathItem[opItem].servers) { - pathItem[opItem].servers.map(server => fixServers(server, filePath)); - } - } - }); - }); - }); - } else { - // Do nothing and return - } -} - -/** - * Determine the proper name for the API specification schema used by a given schema. - * - * @param {object} schema - * @returns {string} - The name of the specification that this schema utilizes. - */ -function getSpecificationName(schema) { - return schema.swagger ? 'Swagger' : 'OpenAPI'; -} - -exports.fixOasRelativeServers = fixOasRelativeServers; -exports.getSpecificationName = getSpecificationName; diff --git a/packages/parser/lib/validators/schema.js b/packages/parser/lib/validators/schema.js deleted file mode 100644 index baaddb0e4..000000000 --- a/packages/parser/lib/validators/schema.js +++ /dev/null @@ -1,174 +0,0 @@ -const { ono } = require('@jsdevtools/ono'); -const betterAjvErrors = require('@readme/better-ajv-errors'); -const { openapi } = require('@readme/openapi-schemas'); -const Ajv = require('ajv/dist/2020'); -const AjvDraft4 = require('ajv-draft-04'); - -const { getSpecificationName } = require('../util'); - -/** - * We've had issues with specs larger than 2MB+ with 1,000+ errors causing memory leaks so if we - * have a spec with more than `LARGE_SPEC_ERROR_CAP` errors and it's **stringified** length is - * larger than `LARGE_SPEC_LIMITS` then we will only return the first `LARGE_SPEC_ERROR_CAP` errors. - * - * Ideally we'd be looking at the byte size of the spec instead of looking at its stringified - * length value but the Blob API, which we'd use to get its size with `new Blob([str]).size;`, was - * only recently introduced in Node 15. - * - * w/r/t the 5,000,000 limit here: The spec we found causing these memory leaks had a size of - * 13,934,323 so 5mil seems like a decent cap to start with. - * - * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Blob} - */ -const LARGE_SPEC_ERROR_CAP = 20; -const LARGE_SPEC_SIZE_CAP = 5000000; - -module.exports = validateSchema; - -/** - * Validates the given Swagger API against the Swagger 2.0 or OpenAPI 3.0 and 3.1 schemas. - * - * @param {SwaggerObject} api - * @param {Object} options - */ -function validateSchema(api, options) { - let ajv; - - // Choose the appropriate schema (Swagger or OpenAPI) - let schema; - - if (api.swagger) { - schema = openapi.v2; - ajv = initializeAjv(); - } else if (api.openapi.startsWith('3.1')) { - schema = openapi.v31legacy; - - // There's a bug with Ajv in how it handles `$dynamicRef` in the way that it's used within the 3.1 schema so we - // need to do some adhoc workarounds. - // https://github.com/OAI/OpenAPI-Specification/issues/2689 - // https://github.com/ajv-validator/ajv/issues/1573 - const schemaDynamicRef = schema.$defs.schema; - delete schemaDynamicRef.$dynamicAnchor; - - schema.$defs.components.properties.schemas.additionalProperties = schemaDynamicRef; - schema.$defs.header.dependentSchemas.schema.properties.schema = schemaDynamicRef; - schema.$defs['media-type'].properties.schema = schemaDynamicRef; - schema.$defs.parameter.properties.schema = schemaDynamicRef; - - ajv = initializeAjv(false); - } else { - schema = openapi.v3; - ajv = initializeAjv(); - } - - // Validate against the schema - const isValid = ajv.validate(schema, api); - if (!isValid) { - const err = ajv.errors; - - let additionalErrors = 0; - let reducedErrors = reduceAjvErrors(err); - const totalErrors = reducedErrors.length; - if (reducedErrors.length >= LARGE_SPEC_ERROR_CAP) { - try { - if (JSON.stringify(api).length >= LARGE_SPEC_SIZE_CAP) { - additionalErrors = reducedErrors.length - 20; - reducedErrors = reducedErrors.slice(0, 20); - } - } catch (error) { - // If we failed to stringify the API definition to look at its size then we should process - // all of its errors as-is. - } - } - - let message = `${getSpecificationName(api)} schema validation failed.\n`; - message += '\n'; - message += betterAjvErrors(schema, api, reducedErrors, { - colorize: options.validate.colorizeErrors, - indent: 2, - }); - - if (additionalErrors) { - message += '\n\n'; - message += `Plus an additional ${additionalErrors} errors. Please resolve the above and re-run validation to see more.`; - } - - throw ono.syntax(err, { details: err, totalErrors }, message); - } -} - -/** - * Determines which version of Ajv to load and prepares it for use. - * - * @param {bool} draft04 - * @returns {Ajv} - */ -function initializeAjv(draft04 = true) { - const opts = { - allErrors: true, - strict: false, - validateFormats: false, - }; - - if (draft04) { - return new AjvDraft4(opts); - } - - return new Ajv(opts); -} - -/** - * Because of the way that Ajv works, if a validation error occurs deep within a schema there's a chance that errors - * will also be thrown for its immediate parents, leading to a case where we'll eventually show the error indecipherable - * errors like "$ref is missing here!" instance of what's _actually_ going on where they may have mistyped `enum` as - * `enumm`. - * - * To alleviate this confusing noise, we're compressing Ajv errors down to only surface the deepest point for each - * lineage, so that if a user typos `enum` as `enumm` we'll surface just that error for them (because really that's - * **the** error). - * - * @param {Array} errors - * @returns {Array} - */ -function reduceAjvErrors(errors) { - const flattened = new Map(); - - errors.forEach(err => { - // These two errors appear when a child schema of them has a problem and instead of polluting the user with - // indecipherable noise we should instead relay the more specific error to them. If this is all that's present in - // the stack then as a safety net before we wrap up we'll just return the original `errors` stack. - if (["must have required property '$ref'", 'must match exactly one schema in oneOf'].includes(err.message)) { - return; - } - - // If this is our first run through let's initialize our dataset and move along. - if (!flattened.size) { - flattened.set(err.instancePath, err); - return; - } else if (flattened.has(err.instancePath)) { - // If we already have an error recorded for this `instancePath` we can ignore it because we (likely) already have - // recorded the more specific error. - return; - } - - // If this error hasn't already been recorded, maybe it's an error against the same `instancePath` stack, in which - // case we should ignore it because the more specific error has already been recorded. - let shouldRecordError = true; - flattened.forEach(flat => { - if (flat.instancePath.includes(err.instancePath)) { - shouldRecordError = false; - } - }); - - if (shouldRecordError) { - flattened.set(err.instancePath, err); - } - }); - - // If we weren't able to fold errors down for whatever reason just return the original stack. - if (!flattened.size) { - return errors; - } - - return [...flattened.values()]; -} diff --git a/packages/parser/lib/validators/spec.js b/packages/parser/lib/validators/spec.js deleted file mode 100644 index c4fe4dba5..000000000 --- a/packages/parser/lib/validators/spec.js +++ /dev/null @@ -1,16 +0,0 @@ -const validateOpenAPI = require('./spec/openapi'); -const validateSwagger = require('./spec/swagger'); - -/** - * Validates either a Swagger 2.0 or OpenAPI 3.x API definition against cases that aren't covered by their JSON Schema - * definitions. - * - * @param {SwaggerObject} api - */ -module.exports = function validateSpec(api) { - if (api.openapi) { - return validateOpenAPI(api); - } - - return validateSwagger(api); -}; diff --git a/packages/parser/package.json b/packages/parser/package.json index dd01a8c46..1989120b3 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -2,6 +2,26 @@ "name": "@readme/openapi-parser", "version": "2.7.0", "description": "Swagger 2.0 and OpenAPI 3.x parser and validator for Node and browsers", + "license": "MIT", + "author": "ReadMe (https://readme.com)", + "sideEffects": false, + "type": "module", + "exports": { + ".": { + "require": "./dist/index.cjs", + "import": "./dist/index.js" + }, + "./package.json": "./package.json" + }, + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.cts", + "engines": { + "node": ">=18" + }, + "files": [ + "dist" + ], "keywords": [ "swagger", "openapi", @@ -19,44 +39,44 @@ "reference", "dereference" ], - "author": { - "name": "James Messinger", - "url": "https://jamesmessinger.com" - }, "repository": { "type": "git", - "url": "https://github.com/readmeio/openapi-parser.git" + "url": "git://github.com/readmeio/oas.git", + "directory": "packages/parser" }, - "license": "MIT", - "main": "lib/index.js", - "typings": "lib/index.d.ts", - "files": [ - "lib" - ], - "engines": { - "node": ">=18" + "bugs": { + "url": "https://github.com/readmeio/oas/issues" }, "scripts": { - "lint": "npm run lint:js", + "attw": "attw --pack --format table-flipped", + "build": "tsup", + "lint": "npm run lint:types && npm run lint:js", "lint:js": "eslint . --ext .js,.ts --ignore-path ../../.gitignore", + "lint:types": "tsc --noEmit", + "prebuild": "rm -rf dist/", + "prepack": "npm run build", "test": "echo 'Please run tests from the root!' && exit 1" }, "dependencies": { + "@apidevtools/json-schema-ref-parser": "^11.9.0", "@jsdevtools/ono": "^7.1.3", "@readme/better-ajv-errors": "^2.1.2", - "@readme/json-schema-ref-parser": "^1.2.0", "@readme/openapi-schemas": "^3.1.0", "ajv": "^8.12.0", - "ajv-draft-04": "^1.0.0" + "ajv-draft-04": "^1.0.0", + "lodash": "^4.17.21" }, "peerDependencies": { "openapi-types": ">=7" }, "devDependencies": { + "@types/lodash": "^4.17.15", "@types/node": "^22.13.1", "eslint": "^8.56.0", "openapi-types": "^12.1.3", - "typescript": "^5.7.3" + "tsup": "^8.3.6", + "typescript": "^5.7.3", + "vitest": "^3.0.5" }, "prettier": "@readme/eslint-config/prettier" } diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts new file mode 100644 index 000000000..6dac3155a --- /dev/null +++ b/packages/parser/src/index.ts @@ -0,0 +1,340 @@ +import type { ParserOptionsStrict } from './options.js'; +import type { Document } from './types.js'; +import type $Refs from '@apidevtools/json-schema-ref-parser/lib/refs'; +import type { $RefsCallback, SchemaCallback } from '@apidevtools/json-schema-ref-parser/lib/types'; +import type { OpenAPI } from 'openapi-types'; + +import $RefParser from '@apidevtools/json-schema-ref-parser'; +import _dereference from '@apidevtools/json-schema-ref-parser/lib/dereference'; +import { normalizeArgs } from '@apidevtools/json-schema-ref-parser/lib/normalize-args'; +import maybe from '@apidevtools/json-schema-ref-parser/lib/util/maybe'; +import { ono } from '@jsdevtools/ono'; + +import { isSwagger, isOpenAPI } from './lib/index.js'; +import { getOptions, ParserOptions } from './options.js'; +import { fixOasRelativeServers } from './util.js'; +import { validateSchema } from './validators/schema.js'; +import { validateSpec } from './validators/spec.js'; + +const supported31Versions = ['3.1.0', '3.1.1']; +const supported30Versions = ['3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.0.4']; +const supportedVersions = [...supported31Versions, ...supported30Versions]; + +export { ParserOptions }; + +/** + * This class parses a Swagger 2.0 or 3.0 OpenAPI API definition, resolves its JSON references and + * their resolved values, and provides methods for traversing, dereferencing, and validating the + * API. + * + * @class + * @augments $RefParser + */ +export class OpenAPIParser extends $RefParser { + /** + * Parses the given Swagger API. + * + * This method does not resolve any JSON references, it just reads a single API definition in + * JSON or YAML format, and parses it as a JS object. + * + * @param {string} [path] - The file path or URL of the JSON schema + * @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`. + * @param {ParserOptions} [options] - ParserOptions that determine how the API is parsed + * @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object. + * @returns {Promise} - The returned promise resolves with the parsed API object. + */ + public override parse(api: S | string): Promise; + public override parse(api: S | string, callback: SchemaCallback): Promise; + public override parse(api: S | string, options: ParserOptions, callback: SchemaCallback): Promise; + public override parse( + path: string, + api: S | string, + options: ParserOptions, + callback: SchemaCallback, + ): Promise; + public override parse(api: S | string, options: ParserOptions): Promise; + public override parse(path: string, api: S | string, options: ParserOptions): Promise; + override async parse() { + const args = normalizeArgs(arguments); + args.options = getOptions(args.options); + + try { + const schema = await super.parse(args.path, args.schema, args.options); + if (!isSwagger(schema) && !isOpenAPI(schema)) { + throw ono.syntax(`${args.path || args.schema} is not a valid API definition`); + } + + if ('swagger' in schema && schema.swagger) { + // Verify that the parsed object is a Swagger API + if (schema.swagger === undefined || schema.info === undefined || schema.paths === undefined) { + throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid Swagger API definition.`); + } else if (typeof schema.swagger === 'number') { + throw ono.syntax('Swagger version number must be a string (e.g. "2.0") not a number.'); + } else if (typeof schema.info.version === 'number') { + throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.'); + } else if (schema.swagger !== '2.0') { + throw ono.syntax(`Unrecognized Swagger version: ${schema.swagger}. Expected 2.0`); + } + } else { + if (!isOpenAPI(schema)) { + throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); + } + + // Verify that the parsed object is a OpenAPI API definition. + if (schema.openapi === undefined || schema.info === undefined) { + throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); + } else if (schema.paths === undefined) { + // An OpenAPI 3.1 definition must have either `paths` or `webhooks`. If it has neither then + // it's invalid. + if (supported31Versions.includes(schema.openapi)) { + if ('webhooks' in schema && schema.webhooks === undefined) { + throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); + } + } else { + throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); + } + } else if (typeof schema.openapi === 'number') { + throw ono.syntax('OpenAPI version number must be a string (e.g. "3.0.0") not a number.'); + } else if (typeof schema.info.version === 'number') { + throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.'); + } else if (!supportedVersions.includes(schema.openapi)) { + throw ono.syntax( + `Unsupported OpenAPI version: ${schema.openapi}. This library only supports versions ${supportedVersions.join(', ')}`, + ); + } + + // This is an OpenAPI v3 schema, check if the "servers" have any relative paths and + // fix them if the content was pulled from a web resource + fixOasRelativeServers(schema, args.path); + } + + return maybe(args.callback, Promise.resolve(schema)); + } catch (err) { + return maybe(args.callback, Promise.reject(err)); + } + } + + /** + * Parses, dereferences, and validates the given Swagger API. + * Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation. + * + * @param {string} [path] - The file path or URL of the JSON schema + * @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`. + * @param {ParserOptions} [options] - ParserOptions that determine how the API is parsed, dereferenced, and validated + * @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object. + * @returns {Promise} - The returned promise resolves with the parsed API object. + */ + public validate(api: S | string, callback: SchemaCallback): Promise; + public validate(api: S | string, options: ParserOptions, callback: SchemaCallback): Promise; + public validate(path: string, api: S | string, options: ParserOptions, callback: SchemaCallback): Promise; + public validate(api: S | string): Promise; + public validate(api: S | string, options: ParserOptions): Promise; + public validate(path: string, api: S | string, options: ParserOptions): Promise; + async validate() { + const args = normalizeArgs(arguments); + args.options = getOptions(args.options); + + // ZSchema doesn't support circular objects, so don't dereference circular $refs yet + // (see https://github.com/zaggino/z-schema/issues/137) + const circular$RefOption = args.options.dereference.circular; + if (args.options.validate.schema) { + args.options.dereference.circular = 'ignore'; + } + + try { + await this.dereference(args.path, args.schema, args.options); + + // Restore the original options, now that we're done dereferencing + args.options.dereference.circular = circular$RefOption; + + if (args.options.validate.schema) { + // Validate the API against the Swagger schema + // NOTE: This is safe to do, because we haven't dereferenced circular $refs yet + validateSchema(this.schema, { + colorizeErrors: args.options.validate.colorizeErrors, + }); + + if (this.$refs?.circular) { + if (circular$RefOption === true) { + // The API has circular references, + // so we need to do a second-pass to fully-dereference it + _dereference(this, args.options); + } else if (circular$RefOption === false) { + // The API has circular references, and they're not allowed, so throw an error + throw ono.reference('The API contains circular references'); + } + } + } + + if (args.options.validate.spec) { + // Validate the API against the Swagger spec + validateSpec(this.schema!); + } + + return maybe(args.callback, Promise.resolve(this.schema)); + } catch (err) { + return maybe(args.callback, Promise.reject(err)); + } + } + + public static validate(schema: OpenAPI.Document | string): Promise; + public static validate( + schema: OpenAPI.Document | string, + callback: SchemaCallback, + ): Promise; + public static validate( + schema: OpenAPI.Document | string, + options: ParserOptions, + ): Promise; + public static validate( + schema: OpenAPI.Document | string, + options: ParserOptions, + callback: SchemaCallback, + ): Promise; + public static validate( + path: string, + schema: OpenAPI.Document | string, + options: ParserOptions, + ): Promise; + public static validate( + path: string, + schema: OpenAPI.Document | string, + options: ParserOptions, + callback: SchemaCallback, + ): Promise; + static validate(): Promise | Promise { + const instance = new OpenAPIParser(); + return instance.validate.apply(instance, arguments); + } + + // + // + // The following methods are monkeypatchers for static methods in `json-schema-ref-parser`. If + // we don't have these static overrides then our instance overrides that have custom behavior + // won't be invoked because `json-schema-ref-parser` static methods instantiate a new instance + // of `json-schema-ref-parser`, not our class. + // + // + + public static parse(schema: S | string | unknown): Promise; + public static parse( + schema: S | string | unknown, + callback: SchemaCallback, + ): Promise; + public static parse = ParserOptions>( + schema: S | string | unknown, + options: O, + ): Promise; + public static parse = ParserOptions>( + schema: S | string | unknown, + options: O, + callback: SchemaCallback, + ): Promise; + public static parse = ParserOptions>( + path: string, + schema: S | string | unknown, + options: O, + ): Promise; + public static parse = ParserOptions>( + path: string, + schema: S | string | unknown, + options: O, + callback: SchemaCallback, + ): Promise; + public static parse(): Promise | Promise { + const instance = new OpenAPIParser(); + return instance.parse.apply(instance, arguments); + } + + public static resolve = ParserOptions>( + schema: S | string | unknown, + ): Promise<$Refs>; + public static resolve = ParserOptions>( + schema: S | string | unknown, + callback: $RefsCallback, + ): Promise; + public static resolve = ParserOptions>( + schema: S | string | unknown, + options: O, + ): Promise<$Refs>; + public static resolve = ParserOptions>( + schema: S | string | unknown, + options: O, + callback: $RefsCallback, + ): Promise; + public static resolve = ParserOptions>( + path: string, + schema: S | string | unknown, + options: O, + ): Promise<$Refs>; + public static resolve = ParserOptions>( + path: string, + schema: S | string | unknown, + options: O, + callback: $RefsCallback, + ): Promise; + static resolve(): Promise | Promise { + const instance = new OpenAPIParser(); + return instance.resolve.apply(instance, arguments); + } + + public static bundle(schema: S | string | unknown): Promise; + public static bundle( + schema: S | string | unknown, + callback: SchemaCallback, + ): Promise; + public static bundle = ParserOptions>( + schema: S | string | unknown, + options: O, + ): Promise; + public static bundle = ParserOptions>( + schema: S | string | unknown, + options: O, + callback: SchemaCallback, + ): Promise; + public static bundle = ParserOptions>( + path: string, + schema: S | string | unknown, + options: O, + ): Promise; + public static bundle = ParserOptions>( + path: string, + schema: S | string | unknown, + options: O, + callback: SchemaCallback, + ): Promise; + static bundle(): Promise | Promise { + const instance = new OpenAPIParser(); + return instance.bundle.apply(instance, arguments); + } + + public static dereference(schema: S | string | unknown): Promise; + public static dereference( + schema: S | string | unknown, + callback: SchemaCallback, + ): Promise; + public static dereference = ParserOptions>( + schema: S | string | unknown, + options: O, + ): Promise; + public static dereference = ParserOptions>( + schema: S | string | unknown, + options: O, + callback: SchemaCallback, + ): Promise; + public static dereference = ParserOptions>( + path: string, + schema: S | string | unknown, + options: O, + ): Promise; + public static dereference = ParserOptions>( + path: string, + schema: S | string | unknown, + options: O, + callback: SchemaCallback, + ): Promise; + static dereference(): Promise | Promise { + const instance = new OpenAPIParser(); + return instance.dereference.apply(instance, arguments); + } +} diff --git a/packages/parser/src/lib/index.ts b/packages/parser/src/lib/index.ts new file mode 100644 index 000000000..7a4b1d4d7 --- /dev/null +++ b/packages/parser/src/lib/index.ts @@ -0,0 +1,45 @@ +import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; + +/** + * Regular expression that matches path parameter templating. + * + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#path-templating} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#path-templating} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-templating} + */ +export const pathParameterTemplateRegExp = /\{([^/}]+)}/g; + +/** + * List of HTTP verbs used for OperationItem as per the OpenAPI and Swagger specifications + * + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#path-item-object} + */ +export const supportedHTTPMethods = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'trace'] as const; +export const swaggerHTTPMethods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'] as const; + +/** + * Is a given object a Swagger API definition? + * + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isSwagger(schema: any): schema is OpenAPIV2.Document { + return 'swagger' in schema && schema.swagger !== undefined; +} + +/** + * Is a given object an OpenAPI API definition? + * + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isOpenAPI(schema: any): schema is OpenAPIV3_1.Document | OpenAPIV3.Document { + return 'openapi' in schema && schema.openapi !== undefined; +} + +/** + * Determine the proper name for the API specification schema used by a given schema. + * + */ +export function getSpecificationName(api: OpenAPIV2.Document | OpenAPIV3_1.Document | OpenAPIV3.Document) { + return isSwagger(api) ? 'Swagger' : 'OpenAPI'; +} diff --git a/packages/parser/src/lib/reduceAjvErrors.ts b/packages/parser/src/lib/reduceAjvErrors.ts new file mode 100644 index 000000000..62546ffaf --- /dev/null +++ b/packages/parser/src/lib/reduceAjvErrors.ts @@ -0,0 +1,57 @@ +import type { ErrorObject } from 'ajv/dist/2020'; + +/** + * Because of the way that Ajv works, if a validation error occurs deep within a schema there's a + * chance that errors will also be thrown for its immediate parents, leading to a case where we'll + * eventually show the error indecipherable errors like "$ref is missing here!" instance of what's + * _actually_ going on where they may have mistyped `enum` as `enumm`. + * + * To alleviate this confusing noise, we're compressing Ajv errors down to only surface the deepest + * point for each lineage, so that if a user typos `enum` as `enumm` we'll surface just that error + * for them (because really that's **the** error). + * + */ +export function reduceAjvErrors(errors: ErrorObject[]) { + const flattened = new Map(); + + errors.forEach(err => { + // These two errors appear when a child schema of them has a problem and instead of polluting + // the user with indecipherable noise we should instead relay the more specific error to them. + // If this is all that's present in the stack then as a safety net before we wrap up we'll just + // return the original `errors` stack. + if (["must have required property '$ref'", 'must match exactly one schema in oneOf'].includes(err.message)) { + return; + } + + // If this is our first run through let's initialize our dataset and move along. + if (!flattened.size) { + flattened.set(err.instancePath, err); + return; + } else if (flattened.has(err.instancePath)) { + // If we already have an error recorded for this `instancePath` we can ignore it because we + // (likely) already have recorded the more specific error. + return; + } + + // If this error hasn't already been recorded, maybe it's an error against the same + // `instancePath` stack, in which case we should ignore it because the more specific error has + // already been recorded. + let shouldRecordError = true; + flattened.forEach(flat => { + if (flat.instancePath.includes(err.instancePath)) { + shouldRecordError = false; + } + }); + + if (shouldRecordError) { + flattened.set(err.instancePath, err); + } + }); + + // If we weren't able to fold errors down for whatever reason just return the original stack. + if (!flattened.size) { + return errors; + } + + return [...flattened.values()]; +} diff --git a/packages/parser/src/options.ts b/packages/parser/src/options.ts new file mode 100644 index 000000000..f937ee604 --- /dev/null +++ b/packages/parser/src/options.ts @@ -0,0 +1,42 @@ +import type { Document } from './types.js'; +import type { SchemaValidator } from './validators/schema.js'; +import type { SpecValidator } from './validators/spec.js'; +import type $RefParserOptions from '@apidevtools/json-schema-ref-parser/lib/options'; +import type { DeepPartial } from '@apidevtools/json-schema-ref-parser/lib/options'; + +import { getNewOptions } from '@apidevtools/json-schema-ref-parser/lib/options'; +import merge from 'lodash/merge'; + +import { validateSchema } from './validators/schema.js'; +import { validateSpec } from './validators/spec.js'; + +export interface ParserOptionsStrict extends $RefParserOptions { + validate: { + colorizeErrors?: boolean; + schema?: SchemaValidator | false; + spec?: SpecValidator | false; + }; +} + +export type ParserOptions = Omit>, 'callback'>; + +function getDefaultOptions(): ParserOptions { + const baseDefaults = getNewOptions({}); + return { + ...baseDefaults, + validate: { + colorizeErrors: false, + schema: validateSchema, + spec: validateSpec, + }, + }; +} + +export function getOptions(options: ParserOptions | object): ParserOptionsStrict { + const newOptions = getDefaultOptions(); + if (options) { + merge(newOptions, options); + } + + return newOptions as ParserOptionsStrict; +} diff --git a/packages/parser/src/types.ts b/packages/parser/src/types.ts new file mode 100644 index 000000000..0ecf54f9f --- /dev/null +++ b/packages/parser/src/types.ts @@ -0,0 +1,6 @@ +import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; + +export type Document> = + | OpenAPIV2.Document + | OpenAPIV3_1.Document + | OpenAPIV3.Document; diff --git a/packages/parser/src/util.ts b/packages/parser/src/util.ts new file mode 100644 index 000000000..1b8f467b0 --- /dev/null +++ b/packages/parser/src/util.ts @@ -0,0 +1,97 @@ +import type { OpenAPI, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; + +import * as url from '@apidevtools/json-schema-ref-parser/lib/util/url'; + +import { isOpenAPI, supportedHTTPMethods } from './lib/index.js'; + +/** + * This function takes in a `ServerObject`, checks if it has relative path and then fixes it as per + * the path URL. + * + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#server-object} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#server-object} + * + * @param server - The server object to be fixed. + * @param path - The path (an HTTP(S) url) from where the file was downloaded. + * @returns The fixed server object + */ +function fixServers( + server: OpenAPIV3_1.ReferenceObject | OpenAPIV3.ParameterObject | OpenAPIV3.ServerObject, + path: string, +) { + // A erver URL starting with "/" tells that it is not an HTTP(s) URL. + if (server && 'url' in server && server.url && server.url.startsWith('/')) { + const inUrl = url.parse(path); + + // eslint-disable-next-line no-param-reassign + server.url = `${inUrl.protocol}//${inUrl.hostname}${server.url}`; + } +} + +/** + * This function helps fix the relative servers in the API definition file be at root, path or + * operation's level. + * + * From the OpenAPI v3 specification for the `ServerObject` `url` property: + * + * REQUIRED. A URL to the target host. This URL supports Server Variables and MAY be relative, + * to indicate that the host location is relative to the location where the OpenAPI document is + * being served. Variable substitutions will be made when a variable is named in `{brackets}`. + * + * Further the spec says that `servers` property can show up at root level, in `PathItemObject` or + * in `OperationObject`. However interpretation of the spec says that relative paths for servers + * should take into account the hostname that serves the OpenAPI file. + * + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#server-object} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#server-object} + */ +export function fixOasRelativeServers(schema: OpenAPI.Document, filePath?: string) { + if (!schema || !isOpenAPI(schema) || !filePath || (!filePath.startsWith('http:') && !filePath.startsWith('https:'))) { + return; + } + + if (schema.servers) { + schema.servers.map(server => fixServers(server, filePath)); // Root level servers array's fixup + } + + (['paths', 'webhooks'] as const).forEach(component => { + if (component in schema) { + const schemaElement = schema.paths || {}; + Object.keys(schemaElement).forEach(path => { + const pathItem = schemaElement[path] || {}; + Object.keys(pathItem).forEach((opItem: keyof typeof pathItem) => { + const pathItemElement = pathItem[opItem]; + if (!pathItemElement) { + return; + } + + /** + * Servers are at the `PathItemObject` level. + * + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#path-item-object} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object} + */ + if (opItem === 'servers' && Array.isArray(pathItemElement)) { + pathItemElement.forEach(server => fixServers(server, filePath)); + return; + } + + /** + * Servers are at the `OperationObject` level. + * + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#operation-object} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#operation-object} + */ + if ( + supportedHTTPMethods.includes(opItem as unknown as (typeof supportedHTTPMethods)[number]) && + typeof pathItemElement === 'object' && + 'servers' in pathItemElement && + Array.isArray(pathItemElement.servers) + ) { + pathItemElement.servers.forEach(server => fixServers(server, filePath)); + } + }); + }); + } + }); +} diff --git a/packages/parser/src/validators/schema.ts b/packages/parser/src/validators/schema.ts new file mode 100644 index 000000000..2428d3749 --- /dev/null +++ b/packages/parser/src/validators/schema.ts @@ -0,0 +1,138 @@ +import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; + +import { ono } from '@jsdevtools/ono'; +import betterAjvErrors from '@readme/better-ajv-errors'; +import { openapi } from '@readme/openapi-schemas'; +import Ajv from 'ajv/dist/2020'; +import AjvDraft4 from 'ajv-draft-04'; + +import { getSpecificationName, isSwagger } from '../lib/index.js'; +import { reduceAjvErrors } from '../lib/reduceAjvErrors.js'; + +export type SchemaValidator = ( + api: OpenAPIV2.Document | OpenAPIV3_1.Document | OpenAPIV3.Document, + options?: { colorizeErrors?: boolean }, +) => void; + +/** + * We've had issues with specs larger than 2MB+ with 1,000+ errors causing memory leaks so if we + * have a spec with more than `LARGE_SPEC_ERROR_CAP` errors and it's **stringified** length is + * larger than `LARGE_SPEC_LIMITS` then we will only return the first `LARGE_SPEC_ERROR_CAP` errors. + * + * Ideally we'd be looking at the byte size of the spec instead of looking at its stringified + * length value but the Blob API, which we'd use to get its size with `new Blob([str]).size;`, was + * only recently introduced in Node 15. + * + * w/r/t the 5,000,000 limit here: The spec we found causing these memory leaks had a size of + * 13,934,323 so 5mil seems like a decent cap to start with. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Blob} + */ +const LARGE_SPEC_ERROR_CAP = 20; +const LARGE_SPEC_SIZE_CAP = 5000000; + +/** + * Determines which version of Ajv to load and prepares it for use. + * + */ +function initializeAjv(draft04: boolean = true) { + const opts = { + allErrors: true, + strict: false, + validateFormats: false, + }; + + if (draft04) { + return new AjvDraft4(opts); + } + + return new Ajv(opts); +} + +/** + * Validates the given Swagger API against the Swagger 2.0 or OpenAPI 3.0 and 3.1 schemas. + * + * @param {SwaggerObject} api + * @param {Object} options + */ +export const validateSchema: SchemaValidator = ( + api: OpenAPIV2.Document | OpenAPIV3_1.Document | OpenAPIV3.Document, + options: { colorizeErrors?: boolean } = {}, +) => { + let ajv; + + // Choose the appropriate schema (Swagger or OpenAPI) + let schema; + + if (isSwagger(api)) { + schema = openapi.v2; + ajv = initializeAjv(); + } else if (api.openapi.startsWith('3.1')) { + schema = openapi.v31legacy; + + /** + * There's a bug with Ajv in how it handles `$dynamicRef` in the way that it's used within the + * 3.1 schema so we need to do some adhoc workarounds. + * + * @see {@link https://github.com/OAI/OpenAPI-Specification/issues/2689} + * @see {@link https://github.com/ajv-validator/ajv/issues/1573} + */ + const schemaDynamicRef = schema.$defs.schema; + if ('$dynamicAnchor' in schemaDynamicRef) { + delete schemaDynamicRef.$dynamicAnchor; + } + + /* eslint-disable @typescript-eslint/ban-ts-comment */ + // @ts-expect-error Intentionally setting up this funky schema for an AJV bug. + schema.$defs.components.properties.schemas.additionalProperties = schemaDynamicRef; + // @ts-expect-error + schema.$defs.header.dependentSchemas.schema.properties.schema = schemaDynamicRef; + // @ts-expect-error + schema.$defs['media-type'].properties.schema = schemaDynamicRef; + // @ts-expect-error + schema.$defs.parameter.properties.schema = schemaDynamicRef; + /* eslint-enable @typescript-eslint/ban-ts-comment */ + + ajv = initializeAjv(false); + } else { + schema = openapi.v3; + ajv = initializeAjv(); + } + + // Validate against the schema + const isValid = ajv.validate(schema, api); + if (!isValid) { + const err = ajv.errors; + + let additionalErrors = 0; + let reducedErrors = reduceAjvErrors(err); + const totalErrors = reducedErrors.length; + if (reducedErrors.length >= LARGE_SPEC_ERROR_CAP) { + try { + if (JSON.stringify(api).length >= LARGE_SPEC_SIZE_CAP) { + additionalErrors = reducedErrors.length - 20; + reducedErrors = reducedErrors.slice(0, 20); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + // If we failed to stringify the API definition to look at its size then we should process + // all of its errors as-is. + } + } + + let message = `${getSpecificationName(api)} schema validation failed.\n`; + message += '\n'; + message += betterAjvErrors(schema, api, reducedErrors, { + colorize: options.colorizeErrors, + indent: 2, + }); + + if (additionalErrors) { + message += '\n\n'; + message += `Plus an additional ${additionalErrors} errors. Please resolve the above and re-run validation to see more.`; + } + + // @ts-expect-error `ono` doens't like the types on this but good news! we're going to get rid of `ono`. + throw ono.syntax(err, { details: err, totalErrors }, message); + } +}; diff --git a/packages/parser/src/validators/spec.ts b/packages/parser/src/validators/spec.ts new file mode 100644 index 000000000..62836a1f1 --- /dev/null +++ b/packages/parser/src/validators/spec.ts @@ -0,0 +1,21 @@ +import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; + +import { isOpenAPI } from '../lib/index.js'; + +import { validateSpec as validateOpenAPI } from './spec/openapi.js'; +import { validateSpec as validateSwagger } from './spec/swagger.js'; + +export type SpecValidator = (api: OpenAPIV2.Document | OpenAPIV3_1.Document | OpenAPIV3.Document) => void; + +/** + * Validates either a Swagger 2.0 or OpenAPI 3.x API definition against cases that aren't covered + * by their JSON Schema definitions. + * + */ +export const validateSpec: SpecValidator = (api: OpenAPIV2.Document | OpenAPIV3_1.Document | OpenAPIV3.Document) => { + if (isOpenAPI(api)) { + return validateOpenAPI(api); + } + + return validateSwagger(api); +}; diff --git a/packages/parser/lib/validators/spec/openapi.js b/packages/parser/src/validators/spec/openapi.ts similarity index 53% rename from packages/parser/lib/validators/spec/openapi.js rename to packages/parser/src/validators/spec/openapi.ts index ab7ba46cc..a78935b02 100644 --- a/packages/parser/lib/validators/spec/openapi.js +++ b/packages/parser/src/validators/spec/openapi.ts @@ -1,149 +1,55 @@ -const { ono } = require('@jsdevtools/ono'); +import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; -const util = require('../../util'); +import { ono } from '@jsdevtools/ono'; -/** - * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object} - */ -const supportedHTTPMethods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']; - -module.exports = validateSpec; - -/** - * Validates parts of the OpenAPI 3.0 and 3.1 that aren't covered by their JSON Schema definitions. - * - * @todo This library currently does not validate required properties like the Swagger validator does due to some - * gnarly quirks with cases where a required property exists within a `oneOf` or `anyOf` (and within a child `allOf` - * of one of those). See https://api.apis.guru/v2/specs/twitter.com/labs/2.13/openapi.yaml for a good example. - * - * @param {SwaggerObject} api - * @link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md - * @link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md - */ -function validateSpec(api) { - const operationIds = []; - Object.keys(api.paths || {}).forEach(pathName => { - const path = api.paths[pathName]; - const pathId = `/paths${pathName}`; +import { supportedHTTPMethods, pathParameterTemplateRegExp } from '../../lib/index.js'; - if (path && pathName.startsWith('/')) { - validatePath(api, path, pathId, operationIds); - } - }); - - // There's a problem with how the 3.0 schema uses `patternProperties` for defining the format of scheme names that it - // ignores anything that doesn't match, so if you for example have a space in a schema name it'll be seen as valid - // when it should instead trigger a validation error. - // - // https://github.com/APIDevTools/swagger-parser/issues/184 - if (api.openapi.startsWith('3.0')) { - if (api.components) { - Object.keys(api.components).forEach(componentType => { - Object.keys(api.components[componentType]).forEach(componentName => { - const componentId = `/components/${componentType}/${componentName}`; - - if (!/^[a-zA-Z0-9.\-_]+$/.test(componentName)) { - throw ono.syntax( - `Validation failed. ${componentId} has an invalid name. Component names should match against: /^[a-zA-Z0-9.-_]+$/`, - ); - } - }); - }); - } - } -} +type ParameterObject = + | (OpenAPIV3_1.ParameterObject | OpenAPIV3_1.ReferenceObject) + | (OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject); /** - * Validates the given path. + * Checks the given parameter list for duplicates. * - * @param {SwaggerObject} api - The entire OpenAPI API definition - * @param {object} path - A Path object, from the OpenAPI API definition - * @param {string} pathId - A value that uniquely identifies the path - * @param {string} operationIds - An array of collected operationIds found in other paths */ -function validatePath(api, path, pathId, operationIds) { - supportedHTTPMethods.forEach(operationName => { - const operation = path[operationName]; - const operationId = `${pathId}/${operationName}`; - - if (operation) { - const declaredOperationId = operation.operationId; - if (declaredOperationId) { - if (!operationIds.includes(declaredOperationId)) { - operationIds.push(declaredOperationId); - } else { - throw ono.syntax(`Validation failed. Duplicate operation id '${declaredOperationId}'`); - } +function checkForDuplicates(params: ParameterObject[]) { + for (let i = 0; i < params.length - 1; i++) { + const outer = params[i]; + for (let j = i + 1; j < params.length; j++) { + const inner = params[j]; + if ('$ref' in outer || '$ref' in inner) { + continue; } - validateParameters(api, path, pathId, operation, operationId); - - Object.keys(operation.responses || {}).forEach(responseCode => { - const response = operation.responses[responseCode]; - const responseId = `${operationId}/responses/${responseCode}`; - validateResponse(responseCode, response || {}, responseId); - }); + if (outer.name === inner.name && outer.in === inner.in) { + throw ono.syntax(`Validation failed. Found multiple ${outer.in} parameters named "${outer.name}"`); + } } - }); + } } /** - * Validates the parameters for the given operation. + * Validates the given Swagger schema object. * - * @param {SwaggerObject} api - The entire Swagger API object - * @param {object} path - A Path object, from the Swagger API - * @param {string} pathId - A value that uniquely identifies the path - * @param {object} operation - An Operation object, from the Swagger API - * @param {string} operationId - A value that uniquely identifies the operation */ -function validateParameters(api, path, pathId, operation, operationId) { - const pathParams = path.parameters || []; - const operationParams = operation.parameters || []; - - // Check for duplicate path parameters. - try { - checkForDuplicates(pathParams); - } catch (e) { - throw ono.syntax(e, `Validation failed. ${pathId} has duplicate parameters`); - } - - // Check for duplicate operation parameters. - try { - checkForDuplicates(operationParams); - } catch (e) { - throw ono.syntax(e, `Validation failed. ${operationId} has duplicate parameters`); +function validateSchema(schema: OpenAPIV3_1.SchemaObject | OpenAPIV3.SchemaObject, schemaId: string) { + if (schema.type === 'array' && !schema.items) { + throw ono.syntax(`Validation failed. ${schemaId} is an array, so it must include an "items" schema`); } - - // Combine the path and operation parameters, with the operation params taking precedence over the path params. - const params = pathParams.reduce((combinedParams, value) => { - const duplicate = combinedParams.some(param => { - return param.in === value.in && param.name === value.name; - }); - - if (!duplicate) { - combinedParams.push(value); - } - - return combinedParams; - }, operationParams.slice()); - - validatePathParameters(params, pathId, operationId); - validateParameterTypes(params, api, operation, operationId); } /** * Validates path parameters for the given path. * - * @param {object[]} params - An array of Parameter objects - * @param {string} pathId - A value that uniquely identifies the path - * @param {string} operationId - A value that uniquely identifies the operation */ -function validatePathParameters(params, pathId, operationId) { - // Find all `{placeholders}` in the path string. And because paths can have path parameters duped we need to convert - // this to a unique array so we can eliminate false positives of placeholders that might be duplicated. - const placeholders = [...new Set(pathId.match(util.swaggerParamRegExp) || [])]; +function validatePathParameters(params: ParameterObject[], pathId: string, operationId: string) { + // Find all `{placeholders}` in the path string. And because paths can have path parameters duped + // we need to convert this to a unique array so we can eliminate false positives of placeholders + // that might be duplicated. + const placeholders = [...new Set(pathId.match(pathParameterTemplateRegExp) || [])]; params + .filter(param => 'in' in param) .filter(param => param.in === 'path') .forEach(param => { if (param.required !== true) { @@ -172,20 +78,22 @@ function validatePathParameters(params, pathId, operationId) { /** * Validates data types of parameters for the given operation. * - * @param {object[]} params - An array of Parameter objects - * @param {object} api - The entire Swagger API object - * @param {object} operation - An Operation object, from the Swagger API - * @param {string} operationId - A value that uniquely identifies the operation */ -function validateParameterTypes(params, api, operation, operationId) { +function validateParameterTypes(params: ParameterObject[], operationId: string) { params.forEach(param => { + if ('$ref' in param) { + return; + } + /** * @todo add better handling when `content` is present instead of `schema`. - * @link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#fixed-fields-10 - * @link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#fixed-fields-10 + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#fixed-fields-10} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#fixed-fields-10} */ if (!param.schema && param.content) { return; + } else if ('$ref' in param.schema) { + return; } const parameterId = `${operationId}/parameters/${param.name}`; @@ -195,40 +103,76 @@ function validateParameterTypes(params, api, operation, operationId) { } /** - * Checks the given parameter list for duplicates, and throws an error if found. + * Validates the parameters for the given operation. * - * @param {object[]} params - An array of Parameter objects */ -function checkForDuplicates(params) { - for (let i = 0; i < params.length - 1; i++) { - const outer = params[i]; - for (let j = i + 1; j < params.length; j++) { - const inner = params[j]; - if (outer.name === inner.name && outer.in === inner.in) { - throw ono.syntax(`Validation failed. Found multiple ${outer.in} parameters named "${outer.name}"`); +function validateParameters( + path: OpenAPIV3_1.PathItemObject | OpenAPIV3.PathItemObject, + pathId: string, + operation: OpenAPIV3_1.OperationObject | OpenAPIV3.OperationObject, + operationId: string, +) { + const pathParams = path.parameters || []; + const operationParams = operation.parameters || []; + + // Check for duplicate path parameters. + try { + checkForDuplicates(pathParams); + } catch (e) { + throw ono.syntax(e, `Validation failed. ${pathId} has duplicate parameters`); + } + + // Check for duplicate operation parameters. + try { + checkForDuplicates(operationParams); + } catch (e) { + throw ono.syntax(e, `Validation failed. ${operationId} has duplicate parameters`); + } + + // Combine the path and operation parameters, with the operation params taking precedence over + // the path params. + const params = pathParams.reduce((combinedParams, value) => { + const duplicate = combinedParams.some(param => { + if ('$ref' in param || '$ref' in value) { + return false; } + + return param.in === value.in && param.name === value.name; + }); + + if (!duplicate) { + combinedParams.push(value); } - } + + return combinedParams; + }, operationParams.slice()); + + validatePathParameters(params, pathId, operationId); + validateParameterTypes(params, operationId); } /** * Validates the given response object. * - * @param {string} code - The HTTP response code (or "default") - * @param {object} response - A Response object, from the Swagger API - * @param {string} responseId - A value that uniquely identifies the response */ -function validateResponse(code, response, responseId) { +function validateResponse(response: OpenAPIV3_1.ResponseObject | OpenAPIV3.ResponseObject, responseId: string) { Object.keys(response.headers || {}).forEach(headerName => { const header = response.headers[headerName]; const headerId = `${responseId}/headers/${headerName}`; + if ('$ref' in header) { + return; + } if (header.schema) { - validateSchema(header.schema, headerId); + if (!('$ref' in header.schema)) { + validateSchema(header.schema, headerId); + } } else if (header.content) { Object.keys(header.content).forEach(mediaType => { if (header.content[mediaType].schema) { - validateSchema(header.content[mediaType].schema || {}, `${headerId}/content/${mediaType}/schema`); + if (!('$ref' in header.content[mediaType].schema)) { + validateSchema(header.content[mediaType].schema || {}, `${headerId}/content/${mediaType}/schema`); + } } }); } @@ -237,20 +181,92 @@ function validateResponse(code, response, responseId) { if (response.content) { Object.keys(response.content).forEach(mediaType => { if (response.content[mediaType].schema) { - validateSchema(response.content[mediaType].schema || {}, `${responseId}/content/${mediaType}/schema`); + if (!('$ref' in response.content[mediaType].schema)) { + validateSchema(response.content[mediaType].schema || {}, `${responseId}/content/${mediaType}/schema`); + } } }); } } /** - * Validates the given Swagger schema object. + * Validates the given path. * - * @param {object} schema - A Schema object, from the Swagger API - * @param {string} schemaId - A value that uniquely identifies the schema object */ -function validateSchema(schema, schemaId) { - if (schema.type === 'array' && !schema.items) { - throw ono.syntax(`Validation failed. ${schemaId} is an array, so it must include an "items" schema`); +function validatePath( + path: OpenAPIV3_1.PathItemObject | OpenAPIV3.PathItemObject, + pathId: string, + operationIds: string[], +) { + supportedHTTPMethods.forEach(operationName => { + const operation = path[operationName]; + const operationId = `${pathId}/${operationName}`; + + if (operation) { + const declaredOperationId = operation.operationId; + if (declaredOperationId) { + if (!operationIds.includes(declaredOperationId)) { + operationIds.push(declaredOperationId); + } else { + throw ono.syntax(`Validation failed. Duplicate operation id '${declaredOperationId}'`); + } + } + + validateParameters(path, pathId, operation, operationId); + + Object.keys(operation.responses || {}).forEach(responseCode => { + const response = operation.responses[responseCode]; + const responseId = `${operationId}/responses/${responseCode}`; + if (response && !('$ref' in response)) { + validateResponse(response, responseId); + } + }); + } + }); +} + +/** + * Validates parts of the OpenAPI 3.0 and 3.1 that aren't covered by their JSON Schema definitions. + * + * @todo This library currently does not validate required properties like the Swagger validator + * does due to some gnarly quirks with cases where a required property exists within a `oneOf` or + * `anyOf` (and within a child `allOf` of one of those). See + * https://api.apis.guru/v2/specs/twitter.com/labs/2.13/openapi.yaml for a good example. + * + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md} + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md} + */ +export function validateSpec(api: OpenAPIV3_1.Document | OpenAPIV3.Document) { + const operationIds: string[] = []; + Object.keys(api.paths || {}).forEach(pathName => { + const path = api.paths[pathName]; + const pathId = `/paths${pathName}`; + + if (path && pathName.startsWith('/')) { + validatePath(path, pathId, operationIds); + } + }); + + /** + * There's a problem with how the 3.0 schema uses `patternProperties` for defining the format of + * scheme names that it ignores anything that doesn't match, so if you for example have a space + * in a schema name it'll be seen as valid when it should instead trigger a validation error. + * + * @see {@link https://github.com/APIDevTools/swagger-parser/issues/184} + */ + if ('openapi' in api && api.openapi.startsWith('3.0')) { + if (api.components) { + Object.keys(api.components).forEach((componentType: keyof typeof api.components) => { + Object.keys(api.components[componentType]).forEach(componentName => { + const componentId = `/components/${componentType}/${componentName}`; + + if (!/^[a-zA-Z0-9.\-_]+$/.test(componentName)) { + throw ono.syntax( + `Validation failed. ${componentId} has an invalid name. Component names should match against: /^[a-zA-Z0-9.-_]+$/`, + ); + } + }); + }); + } } } diff --git a/packages/parser/lib/validators/spec/swagger.js b/packages/parser/src/validators/spec/swagger.ts similarity index 62% rename from packages/parser/lib/validators/spec/swagger.js rename to packages/parser/src/validators/spec/swagger.ts index d23d98d49..e72f7b9de 100644 --- a/packages/parser/lib/validators/spec/swagger.js +++ b/packages/parser/src/validators/spec/swagger.ts @@ -1,131 +1,77 @@ -const { ono } = require('@jsdevtools/ono'); +import type { IJsonSchema, OpenAPIV2 } from 'openapi-types'; -const util = require('../../util'); +import { ono } from '@jsdevtools/ono'; -/** - * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#path-item-object} - */ -const supportedHTTPMethods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch']; - -const primitiveTypes = ['array', 'boolean', 'integer', 'number', 'string']; -const schemaTypes = ['array', 'boolean', 'integer', 'number', 'string', 'object', 'null', undefined]; - -module.exports = validateSpec; +import { swaggerHTTPMethods, pathParameterTemplateRegExp } from '../../lib/index.js'; /** - * Validates parts of the Swagger 2.0 spec that aren't covered by the Swagger 2.0 JSON Schema. + * Checks the given parameter list for duplicates. * - * @param {SwaggerObject} api */ -function validateSpec(api) { - const operationIds = []; - Object.keys(api.paths || {}).forEach(pathName => { - const path = api.paths[pathName]; - const pathId = `/paths${pathName}`; - - if (path && pathName.startsWith('/')) { - validatePath(api, path, pathId, operationIds); - } - }); - - Object.keys(api.definitions || {}).forEach(definitionName => { - const definition = api.definitions[definitionName]; - const definitionId = `/definitions/${definitionName}`; - - if (!/^[a-zA-Z0-9.\-_]+$/.test(definitionName)) { - throw ono.syntax( - `Validation failed. ${definitionId} has an invalid name. Definition names should match against: /^[a-zA-Z0-9.-_]+$/`, - ); +function checkForDuplicates(params: OpenAPIV2.ParameterObject[]) { + for (let i = 0; i < params.length - 1; i++) { + const outer = params[i]; + for (let j = i + 1; j < params.length; j++) { + const inner = params[j]; + if (outer.name === inner.name && outer.in === inner.in) { + throw ono.syntax(`Validation failed. Found multiple ${outer.in} parameters named "${outer.name}"`); + } } - - validateRequiredPropertiesExist(definition, definitionId); - }); + } } /** - * Validates the given path. + * Validates that the declared properties of the given Swagger schema object actually exist. * - * @param {SwaggerObject} api - The entire Swagger API object - * @param {object} path - A Path object, from the Swagger API - * @param {string} pathId - A value that uniquely identifies the path - * @param {string} operationIds - An array of collected operationIds found in other paths */ -function validatePath(api, path, pathId, operationIds) { - supportedHTTPMethods.forEach(operationName => { - const operation = path[operationName]; - const operationId = `${pathId}/${operationName}`; - - if (operation) { - const declaredOperationId = operation.operationId; - if (declaredOperationId) { - if (!operationIds.includes(declaredOperationId)) { - operationIds.push(declaredOperationId); - } else { - throw ono.syntax(`Validation failed. Duplicate operation id '${declaredOperationId}'`); +function validateRequiredPropertiesExist(schema: IJsonSchema, schemaId: string) { + // Recursively collects all properties of the schema and its ancestors. They are added to the props object. + function collectProperties(schemaObj: IJsonSchema, props: Record) { + if (schemaObj.properties) { + Object.keys(schemaObj.properties).forEach(property => { + // eslint-disable-next-line no-prototype-builtins + if (schemaObj.properties.hasOwnProperty(property)) { + // eslint-disable-next-line no-param-reassign + props[property] = schemaObj.properties[property]; } - } - validateParameters(api, path, pathId, operation, operationId); + }); + } - Object.keys(operation.responses || {}).forEach(responseName => { - const response = operation.responses[responseName]; - const responseId = `${operationId}/responses/${responseName}`; - validateResponse(responseName, response || {}, responseId); + if (schemaObj.allOf) { + schemaObj.allOf.forEach(parent => { + collectProperties(parent, props); }); } - }); + } + + if (schema.required && Array.isArray(schema.required)) { + const props: Record = {}; + collectProperties(schema, props); + schema.required.forEach(requiredProperty => { + if (!props[requiredProperty]) { + throw ono.syntax( + `Validation failed. Property '${requiredProperty}' listed as required but does not exist in '${schemaId}'`, + ); + } + }); + } } /** - * Validates the parameters for the given operation. + * Validates the given Swagger schema object. * - * @param {SwaggerObject} api - The entire Swagger API object - * @param {object} path - A Path object, from the Swagger API - * @param {string} pathId - A value that uniquely identifies the path - * @param {object} operation - An Operation object, from the Swagger API - * @param {string} operationId - A value that uniquely identifies the operation */ -function validateParameters(api, path, pathId, operation, operationId) { - const pathParams = path.parameters || []; - const operationParams = operation.parameters || []; - - // Check for duplicate path parameters - try { - checkForDuplicates(pathParams); - } catch (e) { - throw ono.syntax(e, `Validation failed. ${pathId} has duplicate parameters`); - } - - // Check for duplicate operation parameters - try { - checkForDuplicates(operationParams); - } catch (e) { - throw ono.syntax(e, `Validation failed. ${operationId} has duplicate parameters`); +function validateSchema(schema: OpenAPIV2.SchemaObject, schemaId: string) { + if (schema.type === 'array' && !schema.items) { + throw ono.syntax(`Validation failed. ${schemaId} is an array, so it must include an "items" schema`); } - - // Combine the path and operation parameters, - // with the operation params taking precedence over the path params - const params = pathParams.reduce((combinedParams, value) => { - const duplicate = combinedParams.some(param => { - return param.in === value.in && param.name === value.name; - }); - if (!duplicate) { - combinedParams.push(value); - } - return combinedParams; - }, operationParams.slice()); - - validateBodyParameters(params, operationId); - validatePathParameters(params, pathId, operationId); - validateParameterTypes(params, api, operation, operationId); } /** * Validates body and formData parameters for the given operation. * - * @param {object[]} params - An array of Parameter objects - * @param {string} operationId - A value that uniquely identifies the operation */ -function validateBodyParameters(params, operationId) { +function validateBodyParameters(params: OpenAPIV2.ParameterObject[], operationId: string) { const bodyParams = params.filter(param => { return param.in === 'body'; }); @@ -149,13 +95,10 @@ function validateBodyParameters(params, operationId) { /** * Validates path parameters for the given path. * - * @param {object[]} params - An array of Parameter objects - * @param {string} pathId - A value that uniquely identifies the path - * @param {string} operationId - A value that uniquely identifies the operation */ -function validatePathParameters(params, pathId, operationId) { +function validatePathParameters(params: OpenAPIV2.ParameterObject[], pathId: string, operationId: string) { // Find all {placeholders} in the path string - const placeholders = pathId.match(util.swaggerParamRegExp) || []; + const placeholders: string[] = pathId.match(pathParameterTemplateRegExp) || []; // Check for duplicates for (let i = 0; i < placeholders.length; i++) { @@ -195,32 +138,29 @@ function validatePathParameters(params, pathId, operationId) { /** * Validates data types of parameters for the given operation. * - * @param {object[]} params - An array of Parameter objects - * @param {object} api - The entire Swagger API object - * @param {object} operation - An Operation object, from the Swagger API - * @param {string} operationId - A value that uniquely identifies the operation */ -function validateParameterTypes(params, api, operation, operationId) { +function validateParameterTypes( + params: OpenAPIV2.ParameterObject[], + api: OpenAPIV2.Document, + operation: OpenAPIV2.OperationObject, + operationId: string, +) { params.forEach(param => { const parameterId = `${operationId}/parameters/${param.name}`; let schema; - let validTypes; switch (param.in) { case 'body': schema = param.schema; - validTypes = schemaTypes; break; case 'formData': schema = param; - validTypes = primitiveTypes.concat('file'); break; default: schema = param; - validTypes = primitiveTypes; } - validateSchema(schema, parameterId, validTypes); + validateSchema(schema, parameterId); validateRequiredPropertiesExist(schema, parameterId); if (schema.type === 'file') { @@ -245,104 +185,144 @@ function validateParameterTypes(params, api, operation, operationId) { } /** - * Checks the given parameter list for duplicates, and throws an error if found. + * Validates the parameters for the given operation. * - * @param {object[]} params - An array of Parameter objects */ -function checkForDuplicates(params) { - for (let i = 0; i < params.length - 1; i++) { - const outer = params[i]; - for (let j = i + 1; j < params.length; j++) { - const inner = params[j]; - if (outer.name === inner.name && outer.in === inner.in) { - throw ono.syntax(`Validation failed. Found multiple ${outer.in} parameters named "${outer.name}"`); +function validateParameters( + api: OpenAPIV2.Document, + path: OpenAPIV2.PathItemObject, + pathId: string, + operation: OpenAPIV2.OperationObject, + operationId: string, +) { + const pathParams = (path.parameters || []).filter(param => !('$ref' in param)) as OpenAPIV2.ParameterObject[]; + const operationParams = (operation.parameters || []).filter( + param => !('$ref' in param), + ) as OpenAPIV2.ParameterObject[]; + + // Check for duplicate path parameters + try { + checkForDuplicates(pathParams); + } catch (e) { + throw ono.syntax(e, `Validation failed. ${pathId} has duplicate parameters`); + } + + // Check for duplicate operation parameters + try { + checkForDuplicates(operationParams); + } catch (e) { + throw ono.syntax(e, `Validation failed. ${operationId} has duplicate parameters`); + } + + // Combine the path and operation parameters, + // with the operation params taking precedence over the path params + const params = pathParams.reduce((combinedParams, value) => { + const duplicate = combinedParams.some(param => { + if ('$ref' in param || '$ref' in value) { + return false; } + return param.in === value.in && param.name === value.name; + }); + if (!duplicate) { + combinedParams.push(value); } - } + return combinedParams; + }, operationParams.slice()); + + validateBodyParameters(params, operationId); + validatePathParameters(params, pathId, operationId); + validateParameterTypes(params, api, operation, operationId); } /** * Validates the given response object. * - * @param {string} code - The HTTP response code (or "default") - * @param {object} response - A Response object, from the Swagger API - * @param {string} responseId - A value that uniquely identifies the response */ -function validateResponse(code, response, responseId) { - if (code !== 'default' && (code < 100 || code > 599)) { - throw ono.syntax(`Validation failed. ${responseId} has an invalid response code (${code})`); +function validateResponse(code: number | string, response: OpenAPIV2.ResponseObject, responseId: string) { + if (code !== 'default') { + if ( + (typeof code === 'number' && (code < 100 || code > 599)) || + (typeof code === 'string' && (Number(code) < 100 || Number(code) > 599)) + ) { + throw ono.syntax(`Validation failed. ${responseId} has an invalid response code (${code})`); + } } Object.keys(response.headers || {}).forEach(headerName => { const header = response.headers[headerName]; const headerId = `${responseId}/headers/${headerName}`; - validateSchema(header, headerId, primitiveTypes); + validateSchema(header, headerId); }); if (response.schema) { - const validTypes = schemaTypes.concat('file'); - if (!validTypes.includes(response.schema.type)) { - throw ono.syntax( - `Validation failed. ${responseId} has an invalid response schema type (${response.schema.type})`, - ); - } else { - validateSchema(response.schema, `${responseId}/schema`, validTypes); + if ('$ref' in response.schema) { + return; } + + validateSchema(response.schema, `${responseId}/schema`); } } /** - * Validates the given Swagger schema object. + * Validates the given path. * - * @param {object} schema - A Schema object, from the Swagger API - * @param {string} schemaId - A value that uniquely identifies the schema object - * @param {string[]} validTypes - An array of the allowed schema types */ -function validateSchema(schema, schemaId, validTypes) { - if (!validTypes.includes(schema.type)) { - throw ono.syntax(`Validation failed. ${schemaId} has an invalid type (${schema.type})`); - } +function validatePath(api: OpenAPIV2.Document, path: OpenAPIV2.PathItemObject, pathId: string, operationIds: string[]) { + swaggerHTTPMethods.forEach(operationName => { + const operation = path[operationName]; + const operationId = `${pathId}/${operationName}`; - if (schema.type === 'array' && !schema.items) { - throw ono.syntax(`Validation failed. ${schemaId} is an array, so it must include an "items" schema`); - } + if (operation) { + const declaredOperationId = operation.operationId; + if (declaredOperationId) { + if (!operationIds.includes(declaredOperationId)) { + operationIds.push(declaredOperationId); + } else { + throw ono.syntax(`Validation failed. Duplicate operation id '${declaredOperationId}'`); + } + } + validateParameters(api, path, pathId, operation, operationId); + + Object.keys(operation.responses || {}).forEach(responseName => { + const response = operation.responses[responseName]; + if ('$ref' in response || !response) { + return; + } + + const responseId = `${operationId}/responses/${responseName}`; + validateResponse(responseName, response, responseId); + }); + } + }); } /** - * Validates that the declared properties of the given Swagger schema object actually exist. + * Validates parts of the Swagger 2.0 specification that aren't covered by its JSON Schema + * definition. * - * @param {object} schema - A Schema object, from the Swagger API - * @param {string} schemaId - A value that uniquely identifies the schema object + * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md} */ -function validateRequiredPropertiesExist(schema, schemaId) { - // Recursively collects all properties of the schema and its ancestors. They are added to the props object. - function collectProperties(schemaObj, props) { - if (schemaObj.properties) { - Object.keys(schemaObj.properties).forEach(property => { - // eslint-disable-next-line no-prototype-builtins - if (schemaObj.properties.hasOwnProperty(property)) { - // eslint-disable-next-line no-param-reassign - props[property] = schemaObj.properties[property]; - } - }); +export function validateSpec(api: OpenAPIV2.Document) { + const operationIds: string[] = []; + Object.keys(api.paths || {}).forEach(pathName => { + const path = api.paths[pathName]; + const pathId = `/paths${pathName}`; + + if (path && pathName.startsWith('/')) { + validatePath(api, path, pathId, operationIds); } + }); - if (schemaObj.allOf) { - schemaObj.allOf.forEach(parent => { - collectProperties(parent, props); - }); + Object.keys(api.definitions || {}).forEach(definitionName => { + const definition = api.definitions[definitionName]; + const definitionId = `/definitions/${definitionName}`; + + if (!/^[a-zA-Z0-9.\-_]+$/.test(definitionName)) { + throw ono.syntax( + `Validation failed. ${definitionId} has an invalid name. Definition names should match against: /^[a-zA-Z0-9.-_]+$/`, + ); } - } - if (schema.required && Array.isArray(schema.required)) { - const props = {}; - collectProperties(schema, props); - schema.required.forEach(requiredProperty => { - if (!props[requiredProperty]) { - throw ono.syntax( - `Validation failed. Property '${requiredProperty}' listed as required but does not exist in '${schemaId}'`, - ); - } - }); - } + validateRequiredPropertiesExist(definition, definitionId); + }); } diff --git a/packages/parser/test/.eslintrc b/packages/parser/test/.eslintrc index b412a80f8..c5e352781 100644 --- a/packages/parser/test/.eslintrc +++ b/packages/parser/test/.eslintrc @@ -1,7 +1,7 @@ { "extends": "@readme/eslint-config/testing/vitest", "rules": { - "no-console": "off", - "unicorn/no-unsafe-regex": "off" - } + "no-restricted-syntax": "off", + "no-underscore-dangle": "off", + }, } diff --git a/packages/parser/test/specs/awaited-behavior/awaited-behavior.test.ts b/packages/parser/test/specs/awaited-behavior/awaited-behavior.test.ts index 0970aacf0..f9a718567 100644 --- a/packages/parser/test/specs/awaited-behavior/awaited-behavior.test.ts +++ b/packages/parser/test/specs/awaited-behavior/awaited-behavior.test.ts @@ -1,25 +1,11 @@ import { describe, it, expect } from 'vitest'; -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; -import bundledAPI from './bundled'; -import dereferencedAPI from './dereferenced'; -import parsedAPI from './parsed'; - -function getSchema(method: string) { - switch (method) { - case 'parse': - return parsedAPI; - case 'dereference': - case 'validate': - return dereferencedAPI; - case 'bundle': - return bundledAPI; - default: - throw new Error('Unrecognized schema method called.'); - } -} +import bundledAPI from './bundled.js'; +import dereferencedAPI from './dereferenced.js'; +import parsedAPI from './parsed.js'; describe('awaited behavior', () => { describe.each(['parse', 'resolve', 'dereference', 'bundle', 'validate'])('%s method', method => { @@ -36,8 +22,13 @@ describe('awaited behavior', () => { expect(result).to.equal(parser.schema); // Make sure the API was parsed correctly - const expected = getSchema(method); - expect(result).to.deep.equal(expected); + if (method === 'parse') { + expect(result).to.deep.equal(parsedAPI); + } else if (method === 'dereference' || method === 'validate') { + expect(result).to.deep.equal(dereferencedAPI); + } else { + expect(result).to.deep.equal(bundledAPI); + } } }); diff --git a/packages/parser/test/specs/awaited-behavior/bundled.js b/packages/parser/test/specs/awaited-behavior/bundled.ts similarity index 98% rename from packages/parser/test/specs/awaited-behavior/bundled.js rename to packages/parser/test/specs/awaited-behavior/bundled.ts index 535296349..7409c3e62 100644 --- a/packages/parser/test/specs/awaited-behavior/bundled.js +++ b/packages/parser/test/specs/awaited-behavior/bundled.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { swagger: '2.0', info: { version: '1.0.0', diff --git a/packages/parser/test/specs/awaited-behavior/dereferenced.js b/packages/parser/test/specs/awaited-behavior/dereferenced.ts similarity index 99% rename from packages/parser/test/specs/awaited-behavior/dereferenced.js rename to packages/parser/test/specs/awaited-behavior/dereferenced.ts index f76982030..b8e2b7533 100644 --- a/packages/parser/test/specs/awaited-behavior/dereferenced.js +++ b/packages/parser/test/specs/awaited-behavior/dereferenced.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { swagger: '2.0', info: { version: '1.0.0', diff --git a/packages/parser/test/specs/awaited-behavior/parsed.js b/packages/parser/test/specs/awaited-behavior/parsed.ts similarity index 98% rename from packages/parser/test/specs/awaited-behavior/parsed.js rename to packages/parser/test/specs/awaited-behavior/parsed.ts index cf98a04f7..0f91f7b04 100644 --- a/packages/parser/test/specs/awaited-behavior/parsed.js +++ b/packages/parser/test/specs/awaited-behavior/parsed.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { swagger: '2.0', info: { version: '1.0.0', diff --git a/packages/parser/test/specs/better-errors/better-errors.test.ts b/packages/parser/test/specs/better-errors/better-errors.test.ts index c897a1234..ab6b7f904 100644 --- a/packages/parser/test/specs/better-errors/better-errors.test.ts +++ b/packages/parser/test/specs/better-errors/better-errors.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, assert } from 'vitest'; -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; function assertInvalid(file: string, error: string) { return OpenAPIParser.validate(path.rel(`specs/better-errors/${file}`)) @@ -39,8 +39,8 @@ describe('Better errors', () => { assertInvalid('3.1/invalid-x-extension-path.yaml', 'invalid-x-extension is not expected to be here!')); }); - // Due to the JSON Schema changes in OpenAPI 3.1 this case is currently only applicable with Swagger 2.0 and - // OpenAPI 3.0. + // Due to the JSON Schema changes in OpenAPI 3.1 this case is currently only applicable with + // Swagger 2.0 and OpenAPI 3.0. describe('misplaced `additionalProperty`', () => { it('Swagger 2.0', () => assertInvalid('2.0/misplaced-additionalProperty.yaml', 'originalRef is not expected to be here')); @@ -49,9 +49,9 @@ describe('Better errors', () => { assertInvalid('3.0/misplaced-additionalProperty.yaml', 'originalRef is not expected to be here')); }); - // The JSON Schema for OpenAPI 3.1 is the only schema available that can properly detect these within AJV so we're - // only testing that here. OpenAPI 3.0 and Swagger 2.0 have tests cases for this under within the `validate-spec` - // suite. + // The JSON Schema for OpenAPI 3.1 is the only schema available that can properly detect these + // within AJV so we're only testing that here. OpenAPI 3.0 and Swagger 2.0 have tests cases for + // this under within the `validate-spec` suite. describe('invalid component name', () => { it('OpenAPI 3.1', () => assertInvalid('3.1/invalid-component-name.yaml', 'must match pattern ^[a-zA-Z0-9._-]+$')); }); diff --git a/packages/parser/test/specs/circular/bundled.js b/packages/parser/test/specs/circular/bundled.ts similarity index 99% rename from packages/parser/test/specs/circular/bundled.js rename to packages/parser/test/specs/circular/bundled.ts index 9c01147a1..9390ae2f7 100644 --- a/packages/parser/test/specs/circular/bundled.js +++ b/packages/parser/test/specs/circular/bundled.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { swagger: '2.0', info: { version: '1.0.0', diff --git a/packages/parser/test/specs/circular/circular.test.ts b/packages/parser/test/specs/circular/circular.test.ts index 59dc90b84..0333c3ad9 100644 --- a/packages/parser/test/specs/circular/circular.test.ts +++ b/packages/parser/test/specs/circular/circular.test.ts @@ -1,19 +1,22 @@ +import type { ValidAPIDefinition } from '../../utils/helper.js'; + import { describe, it, expect, assert } from 'vitest'; -import OpenAPIParser from '../../..'; -import * as helper from '../../utils/helper'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as helper from '../../utils/helper.js'; +import * as path from '../../utils/path.js'; -import bundledAPI from './bundled'; -import dereferencedAPI from './dereferenced'; -import parsedAPI from './parsed'; -import validatedAPI from './validated'; +import bundledAPI from './bundled.js'; +import dereferencedAPI from './dereferenced.js'; +import parsedAPI from './parsed.js'; +import validatedAPI from './validated.js'; describe('API with circular (recursive) $refs', () => { it('should parse successfully', async () => { const parser = new OpenAPIParser(); const api = await parser.parse(path.rel('specs/circular/circular.yaml')); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(parsedAPI.api); expect(parser.$refs.paths()).to.deep.equal([path.abs('specs/circular/circular.yaml')]); }); @@ -25,56 +28,51 @@ describe('API with circular (recursive) $refs', () => { parsedAPI.api, 'specs/circular/definitions/pet.yaml', parsedAPI.pet, - 'specs/circular/definitions/child.yaml', - parsedAPI.child, - 'specs/circular/definitions/parent.yaml', - parsedAPI.parent, 'specs/circular/definitions/person.yaml', parsedAPI.person, + 'specs/circular/definitions/parent.yaml', + parsedAPI.parent, + 'specs/circular/definitions/child.yaml', + parsedAPI.child, ), ); it('should dereference successfully', async () => { - const parser = new OpenAPIParser(); + const parser = new OpenAPIParser(); const api = await parser.dereference(path.rel('specs/circular/circular.yaml')); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(dereferencedAPI); - // Reference equality expect(api.definitions.person.properties.spouse).to.equal(api.definitions.person); expect(api.definitions.parent.properties.children.items).to.equal(api.definitions.child); expect(api.definitions.child.properties.parents.items).to.equal(api.definitions.parent); }); - // @fixme temporarily skipped due to problems with the upgrade to @apidevtools/json-schema-ref-parser - // eslint-disable-next-line vitest/no-disabled-tests - it.skip('should validate successfully', async () => { - const parser = new OpenAPIParser(); + it('should validate successfully', async () => { + const parser = new OpenAPIParser(); const api = await parser.validate(path.rel('specs/circular/circular.yaml')); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(validatedAPI.fullyDereferenced); - // Reference equality expect(api.definitions.person.properties.spouse).to.equal(api.definitions.person); expect(api.definitions.parent.properties.children.items).to.equal(api.definitions.child); expect(api.definitions.child.properties.parents.items).to.equal(api.definitions.parent); }); - // @fixme temporarily skipped due to problems with the upgrade to @apidevtools/json-schema-ref-parser - // eslint-disable-next-line vitest/no-disabled-tests - it.skip('should not dereference circular $refs if "options.dereference.circular" is "ignore"', async () => { - const parser = new OpenAPIParser(); + it('should not dereference circular $refs if "options.dereference.circular" is "ignore"', async () => { + const parser = new OpenAPIParser(); const api = await parser.validate(path.rel('specs/circular/circular.yaml'), { dereference: { circular: 'ignore' }, }); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(validatedAPI.ignoreCircular$Refs); - // Reference equality expect(api.paths['/pet'].get.responses['200'].schema).to.equal(api.definitions.pet); }); it('should fail validation if "options.dereference.circular" is false', async () => { - const parser = new OpenAPIParser(); - try { + const parser = new OpenAPIParser(); await parser.validate(path.rel('specs/circular/circular.yaml'), { dereference: { circular: false } }); assert.fail(); } catch (err) { @@ -86,7 +84,7 @@ describe('API with circular (recursive) $refs', () => { it('should bundle successfully', async () => { const parser = new OpenAPIParser(); const api = await parser.bundle(path.rel('specs/circular/circular.yaml')); - expect(api).to.equal(parser.api); + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(bundledAPI); }); }); diff --git a/packages/parser/test/specs/circular/dereferenced.js b/packages/parser/test/specs/circular/dereferenced.ts similarity index 98% rename from packages/parser/test/specs/circular/dereferenced.js rename to packages/parser/test/specs/circular/dereferenced.ts index 2db956a4d..2de5e1bbc 100644 --- a/packages/parser/test/specs/circular/dereferenced.js +++ b/packages/parser/test/specs/circular/dereferenced.ts @@ -119,4 +119,4 @@ dereferencedAPI.definitions.parent.properties.children.items = dereferencedAPI.d dereferencedAPI.paths['/parent'].get.responses['200'].schema = dereferencedAPI.definitions.child.properties.parents.items = dereferencedAPI.definitions.parent; -module.exports = dereferencedAPI; +export default dereferencedAPI; diff --git a/packages/parser/test/specs/circular/parsed.js b/packages/parser/test/specs/circular/parsed.ts similarity index 99% rename from packages/parser/test/specs/circular/parsed.js rename to packages/parser/test/specs/circular/parsed.ts index 748ee07df..22cb52744 100644 --- a/packages/parser/test/specs/circular/parsed.js +++ b/packages/parser/test/specs/circular/parsed.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { api: { info: { version: '1.0.0', diff --git a/packages/parser/test/specs/circular/validated.js b/packages/parser/test/specs/circular/validated.ts similarity index 99% rename from packages/parser/test/specs/circular/validated.js rename to packages/parser/test/specs/circular/validated.ts index c245abaed..62d3612e7 100644 --- a/packages/parser/test/specs/circular/validated.js +++ b/packages/parser/test/specs/circular/validated.ts @@ -216,4 +216,4 @@ validatedAPI.fullyDereferenced.paths['/parent'].get.responses['200'].schema = validatedAPI.ignoreCircular$Refs.paths['/pet'].get.responses['200'].schema = validatedAPI.ignoreCircular$Refs.definitions.pet; -module.exports = validatedAPI; +export default validatedAPI; diff --git a/packages/parser/test/specs/colorize-errors-option/colorize-errors-option.test.ts b/packages/parser/test/specs/colorize-errors-option/colorize-errors-option.test.ts index 8af3b242a..b09b65635 100644 --- a/packages/parser/test/specs/colorize-errors-option/colorize-errors-option.test.ts +++ b/packages/parser/test/specs/colorize-errors-option/colorize-errors-option.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, assert } from 'vitest'; -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; describe('`validate.colorizeErrors` option', () => { it('should not colorize errors by default', async () => { diff --git a/packages/parser/test/specs/deep-circular/bundled.js b/packages/parser/test/specs/deep-circular/bundled.ts similarity index 99% rename from packages/parser/test/specs/deep-circular/bundled.js rename to packages/parser/test/specs/deep-circular/bundled.ts index b701f9ae1..8908f9615 100644 --- a/packages/parser/test/specs/deep-circular/bundled.js +++ b/packages/parser/test/specs/deep-circular/bundled.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { swagger: '2.0', info: { version: '1.0.0', diff --git a/packages/parser/test/specs/deep-circular/deep-circular.test.ts b/packages/parser/test/specs/deep-circular/deep-circular.test.ts index c2ccc7212..d38cece8f 100644 --- a/packages/parser/test/specs/deep-circular/deep-circular.test.ts +++ b/packages/parser/test/specs/deep-circular/deep-circular.test.ts @@ -1,18 +1,21 @@ +import type { ValidAPIDefinition } from '../../utils/helper.js'; + import { describe, it, expect } from 'vitest'; -import OpenAPIParser from '../../..'; -import * as helper from '../../utils/helper'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as helper from '../../utils/helper.js'; +import * as path from '../../utils/path.js'; -import bundledAPI from './bundled'; -import dereferencedAPI from './dereferenced'; -import parsedAPI from './parsed'; +import bundledAPI from './bundled.js'; +import dereferencedAPI from './dereferenced.js'; +import parsedAPI from './parsed.js'; describe('API with deeply-nested circular $refs', () => { it('should parse successfully', async () => { const parser = new OpenAPIParser(); const api = await parser.parse(path.rel('specs/deep-circular/deep-circular.yaml')); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(parsedAPI.api); expect(parser.$refs.paths()).to.deep.equal([path.abs('specs/deep-circular/deep-circular.yaml')]); }); @@ -30,11 +33,11 @@ describe('API with deeply-nested circular $refs', () => { ); it('should dereference successfully', async () => { - const parser = new OpenAPIParser(); + const parser = new OpenAPIParser(); const api = await parser.dereference(path.rel('specs/deep-circular/deep-circular.yaml')); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(dereferencedAPI); - // Reference equality expect(api.paths['/family-tree'].get.responses['200'].schema.properties.name.type) .to.equal(api.paths['/family-tree'].get.responses['200'].schema.properties.level1.properties.name.type) .to.equal( @@ -51,11 +54,11 @@ describe('API with deeply-nested circular $refs', () => { }); it('should validate successfully', async () => { - const parser = new OpenAPIParser(); + const parser = new OpenAPIParser(); const api = await parser.validate(path.rel('specs/deep-circular/deep-circular.yaml')); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(dereferencedAPI); - // Reference equality expect(api.paths['/family-tree'].get.responses['200'].schema.properties.name.type) .to.equal(api.paths['/family-tree'].get.responses['200'].schema.properties.level1.properties.name.type) .to.equal( @@ -74,7 +77,8 @@ describe('API with deeply-nested circular $refs', () => { it('should bundle successfully', async () => { const parser = new OpenAPIParser(); const api = await parser.bundle(path.rel('specs/deep-circular/deep-circular.yaml')); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(bundledAPI); }); }); diff --git a/packages/parser/test/specs/deep-circular/dereferenced.js b/packages/parser/test/specs/deep-circular/dereferenced.ts similarity index 99% rename from packages/parser/test/specs/deep-circular/dereferenced.js rename to packages/parser/test/specs/deep-circular/dereferenced.ts index d948aef72..010b71119 100644 --- a/packages/parser/test/specs/deep-circular/dereferenced.js +++ b/packages/parser/test/specs/deep-circular/dereferenced.ts @@ -299,4 +299,4 @@ dereferencedAPI.paths['/family-tree'].get.responses[ ].schema.properties.level1.properties.level2.properties.level3.properties.level4.properties.level5.properties.level6.properties.level7.properties.level8.properties.level9.properties.level10.properties.level11.properties.level12.properties.level13.properties.level14.properties.level15.properties.level16.properties.level17.properties.level18.properties.level19.properties.level20.properties.level21.properties.level22.properties.level23.properties.level24.properties.level25.properties.level26.properties.level27.properties.level28.properties.level29.properties.level30 = dereferencedAPI.paths['/family-tree'].get.responses['200'].schema; -module.exports = dereferencedAPI; +export default dereferencedAPI; diff --git a/packages/parser/test/specs/deep-circular/parsed.js b/packages/parser/test/specs/deep-circular/parsed.ts similarity index 99% rename from packages/parser/test/specs/deep-circular/parsed.js rename to packages/parser/test/specs/deep-circular/parsed.ts index 6dd0b7ba3..a85b3df04 100644 --- a/packages/parser/test/specs/deep-circular/parsed.js +++ b/packages/parser/test/specs/deep-circular/parsed.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { api: { swagger: '2.0', info: { diff --git a/packages/parser/test/specs/exports.test.ts b/packages/parser/test/specs/exports.test.ts deleted file mode 100644 index e78e62a8c..000000000 --- a/packages/parser/test/specs/exports.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { describe, it, expect } from 'vitest'; - -import OpenAPIParser from '../..'; - -describe('Exports', () => { - it('should export the OpenAPIParser class', () => { - expect(OpenAPIParser).to.be.a('function'); - }); - - it('should export all the static methods of OpenAPIParser', () => { - expect(OpenAPIParser.parse).to.be.a('function'); - expect(OpenAPIParser.resolve).to.be.a('function'); - expect(OpenAPIParser.bundle).to.be.a('function'); - expect(OpenAPIParser.dereference).to.be.a('function'); - }); - - it('should export the validate method', () => { - expect(OpenAPIParser.validate).to.be.a('function'); - }); -}); diff --git a/packages/parser/test/specs/invalid/invalid.test.ts b/packages/parser/test/specs/invalid/invalid.test.ts index 1ad506b5f..95938540d 100644 --- a/packages/parser/test/specs/invalid/invalid.test.ts +++ b/packages/parser/test/specs/invalid/invalid.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, assert } from 'vitest'; -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; describe("Invalid APIs (can't be parsed)", () => { it('not a Swagger API', async () => { @@ -10,7 +10,7 @@ describe("Invalid APIs (can't be parsed)", () => { assert.fail(); } catch (err) { expect(err).to.be.an.instanceOf(SyntaxError); - expect(err.message).to.contain('not-swagger.yaml is not a valid OpenAPI definition'); + expect(err.message).to.contain('not-swagger.yaml is not a valid API definition'); } }); diff --git a/packages/parser/test/specs/large-file-memory-leak/large-file-memory-leak.test.ts b/packages/parser/test/specs/large-file-memory-leak/large-file-memory-leak.test.ts index 89d4e4b1d..e113926df 100644 --- a/packages/parser/test/specs/large-file-memory-leak/large-file-memory-leak.test.ts +++ b/packages/parser/test/specs/large-file-memory-leak/large-file-memory-leak.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, assert } from 'vitest'; -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; describe('Large file memory leak protection', { timeout: 20000 }, () => { it.each([ diff --git a/packages/parser/test/specs/oas-relative-servers/v3-relative-servers.test.ts b/packages/parser/test/specs/oas-relative-servers/v3-relative-servers.test.ts index a105854a2..90cd29e33 100644 --- a/packages/parser/test/specs/oas-relative-servers/v3-relative-servers.test.ts +++ b/packages/parser/test/specs/oas-relative-servers/v3-relative-servers.test.ts @@ -1,10 +1,11 @@ +import type { OpenAPIV3 } from 'openapi-types'; import type { MockInstance } from 'vitest'; -import $RefParser from '@readme/json-schema-ref-parser'; +import $RefParser from '@apidevtools/json-schema-ref-parser'; import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; -import OpenAPIParser from '../../../lib'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; // Import of our fixed OpenAPI JSON files import v3NonRelativeServerJson from './v3-non-relative-server.json'; @@ -31,14 +32,14 @@ describe('Servers with relative paths in OpenAPI v3 files', () => { it('should fix relative servers path in the file fetched from url', async () => { spy.mockImplementationOnce(() => JSON.parse(JSON.stringify(v3RelativeServerJson))); - const apiJson = await OpenAPIParser.parse(RELATIVE_SERVERS_OAS3_URL_1); + const apiJson = await OpenAPIParser.parse(RELATIVE_SERVERS_OAS3_URL_1); expect(apiJson.servers[0].url).to.equal('https://petstore3.swagger.io/api/v3'); }); it('should fix relative servers at root, path and operations level in the file fetched from url', async () => { spy.mockImplementationOnce(() => JSON.parse(JSON.stringify(v3RelativeServerPathsOpsJson))); - const apiJson = await OpenAPIParser.parse(RELATIVE_SERVERS_OAS3_URL_2); + const apiJson = await OpenAPIParser.parse(RELATIVE_SERVERS_OAS3_URL_2); expect(apiJson.servers[0].url).to.equal('https://foo.my.cloud/api/v3'); expect(apiJson.paths['/pet'].servers[0].url).to.equal('https://foo.my.cloud/api/v4'); expect(apiJson.paths['/pet'].get.servers[0].url).to.equal('https://foo.my.cloud/api/v5'); @@ -47,7 +48,7 @@ describe('Servers with relative paths in OpenAPI v3 files', () => { it('should parse but no change to relative servers path in local file import', async () => { spy.mockImplementationOnce(() => JSON.parse(JSON.stringify(v3RelativeServerPathsOpsJson))); - const apiJson = await OpenAPIParser.parse(path.rel('./v3-relative-server.json')); + const apiJson = await OpenAPIParser.parse(path.rel('./v3-relative-server.json')); expect(apiJson.servers[0].url).to.equal('/api/v3'); expect(apiJson.paths['/pet'].servers[0].url).to.equal('/api/v4'); expect(apiJson.paths['/pet'].get.servers[0].url).to.equal('/api/v5'); @@ -56,7 +57,7 @@ describe('Servers with relative paths in OpenAPI v3 files', () => { it('should parse but no change to non-relative servers path in local file import', async () => { spy.mockImplementationOnce(() => JSON.parse(JSON.stringify(v3NonRelativeServerJson))); - const apiJson = await OpenAPIParser.parse(path.rel('./v3-non-relative-server.json')); + const apiJson = await OpenAPIParser.parse(path.rel('./v3-non-relative-server.json')); expect(apiJson.servers[0].url).to.equal('https://petstore3.swagger.com/api/v3'); expect(apiJson.paths['/pet'].servers[0].url).to.equal('https://petstore3.swagger.com/api/v4'); expect(apiJson.paths['/pet'].get.servers[0].url).to.equal('https://petstore3.swagger.com/api/v5'); diff --git a/packages/parser/test/specs/object-source/bundled.js b/packages/parser/test/specs/object-source/bundled.ts similarity index 98% rename from packages/parser/test/specs/object-source/bundled.js rename to packages/parser/test/specs/object-source/bundled.ts index 0b3c04ddf..0b298f033 100644 --- a/packages/parser/test/specs/object-source/bundled.js +++ b/packages/parser/test/specs/object-source/bundled.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { swagger: '2.0', info: { version: '1.0.0', diff --git a/packages/parser/test/specs/object-source/dereferenced.js b/packages/parser/test/specs/object-source/dereferenced.ts similarity index 99% rename from packages/parser/test/specs/object-source/dereferenced.js rename to packages/parser/test/specs/object-source/dereferenced.ts index 7be573970..e4ced1b0a 100644 --- a/packages/parser/test/specs/object-source/dereferenced.js +++ b/packages/parser/test/specs/object-source/dereferenced.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { swagger: '2.0', info: { version: '1.0.0', diff --git a/packages/parser/test/specs/object-source/object-source.test.ts b/packages/parser/test/specs/object-source/object-source.test.ts index 4a2c69525..78615e020 100644 --- a/packages/parser/test/specs/object-source/object-source.test.ts +++ b/packages/parser/test/specs/object-source/object-source.test.ts @@ -1,18 +1,22 @@ +import type { ValidAPIDefinition } from '../../utils/helper.js'; + import { describe, it, expect } from 'vitest'; -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; -import bundledAPI from './bundled'; -import dereferencedAPI from './dereferenced'; -import parsedAPI from './parsed'; +import bundledAPI from './bundled.js'; +import dereferencedAPI from './dereferenced.js'; +import parsedAPI from './parsed.js'; describe('Object sources (instead of file paths)', () => { it('should dereference an object that references external files', async () => { - const parser = new OpenAPIParser(); + const parser = new OpenAPIParser(); const api = await parser.dereference(structuredClone(parsedAPI.api)); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(dereferencedAPI); + // The API path should be the current directory, and all other paths should be absolute const expectedPaths = [ path.cwd(), @@ -22,6 +26,7 @@ describe('Object sources (instead of file paths)', () => { ]; expect(parser.$refs.paths()).to.have.same.members(expectedPaths); expect(parser.$refs.values()).to.have.keys(expectedPaths); + // Reference equality expect(api.paths['/people/{name}'].get.responses['200'].schema).to.equal(api.definitions.name); expect(api.definitions.requiredString) @@ -34,8 +39,10 @@ describe('Object sources (instead of file paths)', () => { it('should bundle an object that references external files', async () => { const parser = new OpenAPIParser(); const api = await parser.bundle(structuredClone(parsedAPI.api)); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(bundledAPI); + // The API path should be the current directory, and all other paths should be absolute const expectedPaths = [ path.cwd(), @@ -43,15 +50,18 @@ describe('Object sources (instead of file paths)', () => { path.abs('specs/object-source/definitions/name.yaml'), path.abs('specs/object-source/definitions/required-string.yaml'), ]; + expect(parser.$refs.paths()).to.have.same.members(expectedPaths); expect(parser.$refs.values()).to.have.keys(expectedPaths); }); it('should validate an object that references external files', async () => { - const parser = new OpenAPIParser(); + const parser = new OpenAPIParser(); const api = await parser.dereference(structuredClone(parsedAPI.api)); - expect(api).to.equal(parser.api); + + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(dereferencedAPI); + // The API path should be the current directory, and all other paths should be absolute const expectedPaths = [ path.cwd(), @@ -61,6 +71,7 @@ describe('Object sources (instead of file paths)', () => { ]; expect(parser.$refs.paths()).to.have.same.members(expectedPaths); expect(parser.$refs.values()).to.have.keys(expectedPaths); + // Reference equality expect(api.paths['/people/{name}'].get.responses['200'].schema).to.equal(api.definitions.name); expect(api.definitions.requiredString) diff --git a/packages/parser/test/specs/object-source/parsed.js b/packages/parser/test/specs/object-source/parsed.ts similarity index 98% rename from packages/parser/test/specs/object-source/parsed.js rename to packages/parser/test/specs/object-source/parsed.ts index 0b1d1567d..b8cea2f68 100644 --- a/packages/parser/test/specs/object-source/parsed.js +++ b/packages/parser/test/specs/object-source/parsed.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { api: { swagger: '2.0', info: { diff --git a/packages/parser/test/specs/real-world/known-errors.ts b/packages/parser/test/specs/real-world/known-errors.ts index b1aacaa53..8a7f5bb0d 100644 --- a/packages/parser/test/specs/real-world/known-errors.ts +++ b/packages/parser/test/specs/real-world/known-errors.ts @@ -247,17 +247,14 @@ const knownErrors: KnownError[] = [ export function isKnownError(api: string, error: Error) { for (const knownError of knownErrors) { if (typeof knownError.api === 'string' && !api.includes(knownError.api)) { - // eslint-disable-next-line no-continue continue; } if (typeof knownError.error === 'string' && !error.message.includes(knownError.error)) { - // eslint-disable-next-line no-continue continue; } if (knownError.error instanceof RegExp && !knownError.error.test(error.message)) { - // eslint-disable-next-line no-continue continue; } diff --git a/packages/parser/test/specs/real-world/real-world.test.ts b/packages/parser/test/specs/real-world/real-world.test.ts index 919165cc5..b22fe5840 100644 --- a/packages/parser/test/specs/real-world/real-world.test.ts +++ b/packages/parser/test/specs/real-world/real-world.test.ts @@ -1,9 +1,9 @@ import { describe, it } from 'vitest'; -import OpenAPIParser from '../../..'; +import { OpenAPIParser } from '../../../src/index.js'; import realWorldAPIs from '../../fixtures/real-world-apis.json'; -import { isKnownError } from './known-errors'; +import { isKnownError } from './known-errors.js'; const MAX_APIS_TO_TEST = 100; diff --git a/packages/parser/test/specs/typescript-definition.spec.ts b/packages/parser/test/specs/typescript-definition.spec.ts deleted file mode 100644 index 0e288d737..000000000 --- a/packages/parser/test/specs/typescript-definition.spec.ts +++ /dev/null @@ -1,174 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable vitest/require-hook */ -/* eslint-disable vitest/consistent-test-filename */ -import type { OpenAPI } from 'openapi-types'; - -import * as assert from 'assert'; - -import * as OpenAPIParser from '../../lib'; - -const baseUrl = 'http://example.com/api'; -const openapiPath = 'my-api.json'; -const options = {}; -const promiseResolve = (_: object) => undefined; -const promiseReject = (_: Error) => undefined; -const callback = (_err: Error | null, _api?: object) => undefined; -const openapiObject: OpenAPI.Document = { - openapi: '3.0.0', - info: { - title: 'My API', - version: '1.0.0', - }, - paths: {}, -}; - -// OpenAPIParser class instance -const parser = new OpenAPIParser(); - -// OpenAPIParser instance properties -assert(parser.$refs.circular === true); -assert(parser.api.info.title === 'My API'); - -// OpenAPIParser instance methods (with callbacks) -parser.bundle(openapiPath, callback); -parser.bundle(openapiObject, callback); -parser.bundle(openapiPath, options, callback); -parser.bundle(openapiObject, options, callback); -parser.bundle(baseUrl, openapiPath, options, callback); -parser.bundle(baseUrl, openapiObject, options, callback); - -parser.dereference(openapiPath, callback); -parser.dereference(openapiObject, callback); -parser.dereference(openapiPath, options, callback); -parser.dereference(openapiObject, options, callback); -parser.dereference(baseUrl, openapiPath, options, callback); -parser.dereference(baseUrl, openapiObject, options, callback); - -parser.validate(openapiPath, callback); -parser.validate(openapiObject, callback); -parser.validate(openapiPath, options, callback); -parser.validate(openapiObject, options, callback); -parser.validate(baseUrl, openapiPath, options, callback); -parser.validate(baseUrl, openapiObject, options, callback); - -parser.parse(openapiPath, callback); -parser.parse(openapiObject, callback); -parser.parse(openapiPath, options, callback); -parser.parse(openapiObject, options, callback); -parser.parse(baseUrl, openapiPath, options, callback); -parser.parse(baseUrl, openapiObject, options, callback); - -parser.resolve(openapiPath, callback); -parser.resolve(openapiObject, callback); -parser.resolve(openapiPath, options, callback); -parser.resolve(openapiObject, options, callback); -parser.resolve(baseUrl, openapiPath, options, callback); -parser.resolve(baseUrl, openapiObject, options, callback); - -// OpenAPIParser instance methods (with Promises) -parser.bundle(openapiPath).then(promiseResolve, promiseReject); -parser.bundle(openapiObject).then(promiseResolve, promiseReject); -parser.bundle(openapiPath, options).then(promiseResolve, promiseReject); -parser.bundle(openapiObject, options).then(promiseResolve, promiseReject); -parser.bundle(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -parser.bundle(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -parser.dereference(openapiPath).then(promiseResolve, promiseReject); -parser.dereference(openapiObject).then(promiseResolve, promiseReject); -parser.dereference(openapiPath, options).then(promiseResolve, promiseReject); -parser.dereference(openapiObject, options).then(promiseResolve, promiseReject); -parser.dereference(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -parser.dereference(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -parser.validate(openapiPath).then(promiseResolve, promiseReject); -parser.validate(openapiObject).then(promiseResolve, promiseReject); -parser.validate(openapiPath, options).then(promiseResolve, promiseReject); -parser.validate(openapiObject, options).then(promiseResolve, promiseReject); -parser.validate(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -parser.validate(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -parser.parse(openapiPath).then(promiseResolve, promiseReject); -parser.parse(openapiObject).then(promiseResolve, promiseReject); -parser.parse(openapiPath, options).then(promiseResolve, promiseReject); -parser.parse(openapiObject, options).then(promiseResolve, promiseReject); -parser.parse(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -parser.parse(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -parser.resolve(openapiPath).then(promiseResolve, promiseReject); -parser.resolve(openapiObject).then(promiseResolve, promiseReject); -parser.resolve(openapiPath, options).then(promiseResolve, promiseReject); -parser.resolve(openapiObject, options).then(promiseResolve, promiseReject); -parser.resolve(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -parser.resolve(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -// OpenAPIParser static methods (with callbacks) -OpenAPIParser.bundle(openapiPath, callback); -OpenAPIParser.bundle(openapiObject, callback); -OpenAPIParser.bundle(openapiPath, options, callback); -OpenAPIParser.bundle(openapiObject, options, callback); -OpenAPIParser.bundle(baseUrl, openapiPath, options, callback); -OpenAPIParser.bundle(baseUrl, openapiObject, options, callback); - -OpenAPIParser.dereference(openapiPath, callback); -OpenAPIParser.dereference(openapiObject, callback); -OpenAPIParser.dereference(openapiPath, options, callback); -OpenAPIParser.dereference(openapiObject, options, callback); -OpenAPIParser.dereference(baseUrl, openapiPath, options, callback); -OpenAPIParser.dereference(baseUrl, openapiObject, options, callback); - -OpenAPIParser.validate(openapiPath, callback); -OpenAPIParser.validate(openapiObject, callback); -OpenAPIParser.validate(openapiPath, options, callback); -OpenAPIParser.validate(openapiObject, options, callback); -OpenAPIParser.validate(baseUrl, openapiPath, options, callback); -OpenAPIParser.validate(baseUrl, openapiObject, options, callback); - -OpenAPIParser.parse(openapiPath, callback); -OpenAPIParser.parse(openapiObject, callback); -OpenAPIParser.parse(openapiPath, options, callback); -OpenAPIParser.parse(openapiObject, options, callback); -OpenAPIParser.parse(baseUrl, openapiPath, options, callback); -OpenAPIParser.parse(baseUrl, openapiObject, options, callback); - -OpenAPIParser.resolve(openapiPath, callback); -OpenAPIParser.resolve(openapiObject, callback); -OpenAPIParser.resolve(openapiPath, options, callback); -OpenAPIParser.resolve(openapiObject, options, callback); -OpenAPIParser.resolve(baseUrl, openapiPath, options, callback); -OpenAPIParser.resolve(baseUrl, openapiObject, options, callback); - -// OpenAPIParser static methods (with Promises) -OpenAPIParser.bundle(openapiPath).then(promiseResolve, promiseReject); -OpenAPIParser.bundle(openapiObject).then(promiseResolve, promiseReject); -OpenAPIParser.bundle(openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.bundle(openapiObject, options).then(promiseResolve, promiseReject); -OpenAPIParser.bundle(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.bundle(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -OpenAPIParser.dereference(openapiPath).then(promiseResolve, promiseReject); -OpenAPIParser.dereference(openapiObject).then(promiseResolve, promiseReject); -OpenAPIParser.dereference(openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.dereference(openapiObject, options).then(promiseResolve, promiseReject); -OpenAPIParser.dereference(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.dereference(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -OpenAPIParser.validate(openapiPath).then(promiseResolve, promiseReject); -OpenAPIParser.validate(openapiObject).then(promiseResolve, promiseReject); -OpenAPIParser.validate(openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.validate(openapiObject, options).then(promiseResolve, promiseReject); -OpenAPIParser.validate(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.validate(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -OpenAPIParser.parse(openapiPath).then(promiseResolve, promiseReject); -OpenAPIParser.parse(openapiObject).then(promiseResolve, promiseReject); -OpenAPIParser.parse(openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.parse(openapiObject, options).then(promiseResolve, promiseReject); -OpenAPIParser.parse(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.parse(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); - -OpenAPIParser.resolve(openapiPath).then(promiseResolve, promiseReject); -OpenAPIParser.resolve(openapiObject).then(promiseResolve, promiseReject); -OpenAPIParser.resolve(openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.resolve(openapiObject, options).then(promiseResolve, promiseReject); -OpenAPIParser.resolve(baseUrl, openapiPath, options).then(promiseResolve, promiseReject); -OpenAPIParser.resolve(baseUrl, openapiObject, options).then(promiseResolve, promiseReject); diff --git a/packages/parser/test/specs/unknown/dereferenced.js b/packages/parser/test/specs/unknown/dereferenced.ts similarity index 98% rename from packages/parser/test/specs/unknown/dereferenced.js rename to packages/parser/test/specs/unknown/dereferenced.ts index c7835da4c..d45b04633 100644 --- a/packages/parser/test/specs/unknown/dereferenced.js +++ b/packages/parser/test/specs/unknown/dereferenced.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { swagger: '2.0', info: { version: '1.0.0', @@ -27,7 +27,7 @@ module.exports = { schema: { type: 'file', default: - '\n\n \n \n \n\n\n

Hello World

\n\n\n', + '\n\n \n \n \n \n \n

Hello World

\n \n\n', }, }, }, diff --git a/packages/parser/test/specs/unknown/parsed.js b/packages/parser/test/specs/unknown/parsed.ts similarity index 99% rename from packages/parser/test/specs/unknown/parsed.js rename to packages/parser/test/specs/unknown/parsed.ts index add47d6c2..8ae1d01b5 100644 --- a/packages/parser/test/specs/unknown/parsed.js +++ b/packages/parser/test/specs/unknown/parsed.ts @@ -1,4 +1,4 @@ -module.exports = { +export default { api: { swagger: '2.0', info: { diff --git a/packages/parser/test/specs/unknown/unknown.test.ts b/packages/parser/test/specs/unknown/unknown.test.ts index 755735c28..541a7b9d5 100644 --- a/packages/parser/test/specs/unknown/unknown.test.ts +++ b/packages/parser/test/specs/unknown/unknown.test.ts @@ -1,18 +1,20 @@ +import type { ValidAPIDefinition } from '../../utils/helper.js'; + import { describe, it, expect } from 'vitest'; -import OpenAPIParser from '../../..'; -import * as helper from '../../utils/helper'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as helper from '../../utils/helper.js'; +import * as path from '../../utils/path.js'; -import dereferencedAPI from './dereferenced'; -import parsedAPI from './parsed'; +import dereferencedAPI from './dereferenced.js'; +import parsedAPI from './parsed.js'; describe('API with $refs to unknown file types', () => { it('should parse successfully', async () => { const parser = new OpenAPIParser(); const api = await parser.parse(path.rel('specs/unknown/unknown.yaml')); - expect(api).to.equal(parser.api); + expect(api).to.equal(parser.schema); expect(api).to.deep.equal(parsedAPI.api); expect(parser.$refs.paths()).to.deep.equal([path.abs('specs/unknown/unknown.yaml')]); }); @@ -34,71 +36,53 @@ describe('API with $refs to unknown file types', () => { ); it('should dereference successfully', async () => { - const parser = new OpenAPIParser(); + const parser = new OpenAPIParser(); const api = await parser.dereference(path.rel('specs/unknown/unknown.yaml')); - expect(api).to.equal(parser.api); - - api.paths['/files/text'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/text'].get.responses['200'].default, - ); - - api.paths['/files/html'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/html'].get.responses['200'].default, + expect(api).to.equal(parser.schema); + expect(api.paths['/files/text'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/text'].get.responses['200'].schema.default, ); - - api.paths['/files/blank'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/blank'].get.responses['200'].default, + expect(api.paths['/files/html'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/html'].get.responses['200'].schema.default, ); - - api.paths['/files/binary'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/binary'].get.responses['200'].default, + expect(api.paths['/files/blank'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/blank'].get.responses['200'].schema.default, ); + expect(api.paths['/files/binary'].get.responses['200'].schema.default).to.be.an.instanceOf(Buffer); }); it('should validate successfully', async () => { - const parser = new OpenAPIParser(); + const parser = new OpenAPIParser(); const api = await parser.validate(path.rel('specs/unknown/unknown.yaml')); - expect(api).to.equal(parser.api); - - api.paths['/files/text'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/text'].get.responses['200'].default, + expect(api).to.equal(parser.schema); + expect(api.paths['/files/text'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/text'].get.responses['200'].schema.default, ); - - api.paths['/files/html'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/html'].get.responses['200'].default, + expect(api.paths['/files/html'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/html'].get.responses['200'].schema.default, ); - - api.paths['/files/blank'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/blank'].get.responses['200'].default, - ); - - api.paths['/files/binary'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/binary'].get.responses['200'].default, + expect(api.paths['/files/blank'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/blank'].get.responses['200'].schema.default, ); + expect(api.paths['/files/binary'].get.responses['200'].schema.default).to.be.an.instanceOf(Buffer); }); it('should bundle successfully', async () => { - const parser = new OpenAPIParser(); + const parser = new OpenAPIParser(); const api = await parser.bundle(path.rel('specs/unknown/unknown.yaml')); - expect(api).to.equal(parser.api); - - api.paths['/files/text'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/text'].get.responses['200'].default, + expect(api).to.equal(parser.schema); + expect(api.paths['/files/text'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/text'].get.responses['200'].schema.default, ); - - api.paths['/files/html'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/html'].get.responses['200'].default, - ); - - api.paths['/files/blank'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/blank'].get.responses['200'].default, + expect(api.paths['/files/html'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/html'].get.responses['200'].schema.default, ); - - api.paths['/files/binary'].get.responses['200'].default = helper.convertNodeBuffersToPOJOs( - dereferencedAPI.paths['/files/binary'].get.responses['200'].default, + expect(api.paths['/files/blank'].get.responses['200'].schema.default).to.equal( + dereferencedAPI.paths['/files/blank'].get.responses['200'].schema.default, ); + expect(api.paths['/files/binary'].get.responses['200'].schema.default).to.be.an.instanceOf(Buffer); }); }); diff --git a/packages/parser/test/specs/validate-schema/validate-schema.test.ts b/packages/parser/test/specs/validate-schema/validate-schema.test.ts index 07d7d2d69..c3de75aba 100644 --- a/packages/parser/test/specs/validate-schema/validate-schema.test.ts +++ b/packages/parser/test/specs/validate-schema/validate-schema.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, assert } from 'vitest'; -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; describe('Invalid APIs (Swagger 2.0 and OpenAPI 3.x schema validation)', () => { it('should pass validation if "options.validate.schema" is false', async () => { @@ -145,16 +145,4 @@ describe('Invalid APIs (Swagger 2.0 and OpenAPI 3.x schema validation)', () => { expect(details.message).to.be.a('string').with.length.of.at.least(1); // must match exactly one schema in oneOf } }); - - // for (const test of tests) { - // if (test.valid) { - // it(test.name, async () => { - - // }); - // } else { - // it(test.name, async () => { - - // }); - // } - // } }); diff --git a/packages/parser/test/specs/validate-spec/validate-spec.test.ts b/packages/parser/test/specs/validate-spec/validate-spec.test.ts index 19b1d157e..34e9c82f2 100644 --- a/packages/parser/test/specs/validate-spec/validate-spec.test.ts +++ b/packages/parser/test/specs/validate-spec/validate-spec.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, assert } from 'vitest'; -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; +import { OpenAPIParser } from '../../../src/index.js'; +import * as path from '../../utils/path.js'; function assertValid(file: string) { return OpenAPIParser.validate(path.rel(`specs/validate-spec/valid/${file}`)).then(api => { @@ -297,16 +297,20 @@ describe('Invalid APIs (specification validation)', () => { }); describe('should catch invalid discriminators', () => { - // Invalid discriminators are only **not** picked up with the 3.1 spec, so for 3.0 we can fall back to our normal - // schema validation -- which'll give us a different error message. + // Invalid discriminators are only **not** picked up with the 3.1 spec, so for 3.0 we can fall + // back to our normal schema validation -- which'll give us a different error message. it('OpenAPI 3.0', () => { return assertInvalid('3.0/invalid-discriminator.yaml', 'type must be object'); }); - // @todo We can't yet write validation for this because our OpenAPI (and Swagger) spec validators don't have the - // best, or fastest, handling for nested schemas. It would likely be easier and faster to use something like - // `jsonpath` but that library unfortunately would add a lot of bloat to this library and it doesn't play well with - // browsers. + /** + * We can't yet write validation for this because our OpenAPI (and Swagger) spec validators + * don't have the best, or fastest, handling for nested schemas. It would likely be easier and + * faster to use something like `jsonpath` but that library unfortunately would add a lot of + * bloat to this library and it doesn't play well with browsers. + * + * @todo + */ // eslint-disable-next-line vitest/no-disabled-tests it.skip('OpenAPI 3.1', () => { return assertInvalid('3.1/invalid-discriminator.yaml', 'TKTK'); diff --git a/packages/parser/test/tsconfig.json b/packages/parser/test/tsconfig.json new file mode 100644 index 000000000..f044707f9 --- /dev/null +++ b/packages/parser/test/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "ES2020", + "noImplicitAny": false + }, + "include": ["./**/*"] +} diff --git a/packages/parser/test/utils/helper.ts b/packages/parser/test/utils/helper.ts index 7bee3a7dd..62f6562a4 100644 --- a/packages/parser/test/utils/helper.ts +++ b/packages/parser/test/utils/helper.ts @@ -1,14 +1,26 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { IJsonSchema, OpenAPI } from 'openapi-types'; + import { expect } from 'vitest'; -import OpenAPIParser from '../..'; +import { OpenAPIParser } from '../../src/index.js'; + +import * as path from './path.js'; -import path from './path'; +/** + * A general purpose "this API definition is definitely valid" type that will allow you to bypass + * any type quirks on _real_ OpenAPI v2 and v3 types where sometimes objects can be a + * `ReferenceObject` and you know the data you have doesn't have that. + * + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ValidAPIDefinition = any; /** - * Converts Buffer objects to POJOs, so they can be compared using Chai + * Converts Buffer objects to POJOs, so they can be compared using Chai. + * */ -export function convertNodeBuffersToPOJOs(value) { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function convertNodeBuffersToPOJOs(value: any) { if (value && (value._isBuffer || (value.constructor && value.constructor.name === 'Buffer'))) { // Convert Buffers to POJOs for comparison // eslint-disable-next-line no-param-reassign @@ -19,18 +31,22 @@ export function convertNodeBuffersToPOJOs(value) { } /** - * Tests the {@link OpenAPIParser.resolve} method, - * and asserts that the given file paths resolve to the given values. + * Tests the `OpenAPIParser.resolve` method, and asserts that the given file paths resolve to the + * given values. * * @param {string} filePath - The file path that should be resolved * @param {*} resolvedValue - The resolved value of the file * @param {...*} [params] - Additional file paths and resolved values * @returns {Function} */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export function testResolve(filePath: string, resolvedValue: any, ...params: any | string) { - const schemaFile = path.rel(arguments[0]); - const parsedAPI = arguments[1]; +export function testResolve( + filePath: string, + resolvedValue: OpenAPI.Document, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ...params: (IJsonSchema | string)[] +) { + const schemaFile = path.rel(filePath); + const parsedAPI = resolvedValue; const expectedFiles: string[] = []; const expectedValues: string[] = []; for (let i = 0; i < arguments.length; i++) { @@ -42,12 +58,12 @@ export function testResolve(filePath: string, resolvedValue: any, ...params: any const parser = new OpenAPIParser(); const $refs = await parser.resolve(schemaFile); - expect(parser.api).to.deep.equal(parsedAPI); + expect(parser.schema).to.deep.equal(parsedAPI); expect(parser.$refs).to.equal($refs); // Resolved file paths - expect($refs.paths()).to.have.same.members(expectedFiles); - expect($refs.paths(['file'])).to.have.same.members(expectedFiles); + expect($refs.paths()).toStrictEqual(expectedFiles); + expect($refs.paths(['file'])).toStrictEqual(expectedFiles); expect($refs.paths('http')).to.be.an('array').with.lengthOf(0); // Resolved values diff --git a/packages/parser/test/utils/path.js b/packages/parser/test/utils/path.js deleted file mode 100644 index 7447397f8..000000000 --- a/packages/parser/test/utils/path.js +++ /dev/null @@ -1,55 +0,0 @@ -const nodePath = require('path'); -const nodeUrl = require('url'); - -const testsDir = nodePath.resolve(__dirname, '..'); -const isWindows = /^win/.test(process.platform); - -// Run all tests from the "test" directory -// eslint-disable-next-line vitest/require-hook -process.chdir(nodePath.join(__dirname, '..')); - -/** - * Helper functions for getting local filesystem paths in various formats - */ -const path = { - /** - * Returns the relative path of a file in the "test" directory - */ - rel(file) { - return nodePath.normalize(file); - }, - - /** - * Returns the absolute path of a file in the "test" directory - */ - abs(file) { - return nodePath.join(testsDir, file || nodePath.sep); - }, - - /** - * Returns the path of a file in the "test" directory as a URL. - * (e.g. "file://path/to/json-schema-ref-parser/test/files...") - */ - url(file) { - let pathname = path.abs(file); - - if (isWindows) { - pathname = pathname.replace(/\\/g, '/'); // Convert Windows separators to URL separators - } - - return nodeUrl.format({ - protocol: 'file:', - slashes: true, - pathname, - }); - }, - - /** - * Returns the absolute path of the current working directory. - */ - cwd() { - return nodePath.join(process.cwd(), nodePath.sep); - }, -}; - -module.exports = path; diff --git a/packages/parser/test/utils/path.ts b/packages/parser/test/utils/path.ts new file mode 100644 index 000000000..4a1b6456a --- /dev/null +++ b/packages/parser/test/utils/path.ts @@ -0,0 +1,28 @@ +import nodePath from 'node:path'; + +const __dirname = import.meta.dirname; + +/** + * Returns the relative path of a file in the `test/` directory. + * + */ +export function rel(file: string) { + return nodePath.normalize(file); +} + +/** + * Returns the absolute path of a file in the `test/` directory. + * + */ +export function abs(file: string) { + const testsDir = nodePath.resolve(__dirname, '..'); + return nodePath.join(testsDir, file || nodePath.sep); +} + +/** + * Returns the absolute path of the current working directory. + * + */ +export function cwd() { + return nodePath.join(process.cwd(), nodePath.sep); +} diff --git a/packages/parser/test/vitest.setup.ts b/packages/parser/test/vitest.setup.ts new file mode 100644 index 000000000..606be7a8c --- /dev/null +++ b/packages/parser/test/vitest.setup.ts @@ -0,0 +1,11 @@ +import nodePath from 'node:path'; + +import { beforeAll } from 'vitest'; + +const __dirname = import.meta.dirname; + +beforeAll(() => { + // Because this library tests do a lot of processing on **relative** file paths in order for + // these tests need to function properly they need to be run from the `test/` directory. + process.chdir(nodePath.join(__dirname)); +}); diff --git a/packages/parser/tsconfig.json b/packages/parser/tsconfig.json new file mode 100644 index 000000000..69a3506ab --- /dev/null +++ b/packages/parser/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + // DOM is needed because `@apidevtools/json-schema-ref-parser` uses `window` and `location`. + "lib": ["DOM"] + }, + "include": ["./src/**/*"], + "exclude": ["dist"] +} diff --git a/packages/parser/tsup.config.ts b/packages/parser/tsup.config.ts new file mode 100644 index 000000000..6412d76a0 --- /dev/null +++ b/packages/parser/tsup.config.ts @@ -0,0 +1,14 @@ +import type { Options } from 'tsup'; + +// eslint-disable-next-line import/no-extraneous-dependencies +import { defineConfig } from 'tsup'; + +import config from '../../tsup.config.js'; + +export default defineConfig((options: Options) => ({ + ...options, + ...config, + + entry: ['src/index.ts'], + silent: !options.watch, +})); diff --git a/packages/parser/vitest.config.mts b/packages/parser/vitest.config.mts index 5160e872e..7356bbc69 100644 --- a/packages/parser/vitest.config.mts +++ b/packages/parser/vitest.config.mts @@ -9,22 +9,6 @@ export default defineConfig({ // https://github.com/chalk/supports-color/issues/106 FORCE_COLOR: '1', }, - - exclude: [ - '**/node_modules/**', - '**/dist/**', - - // This test is better served by native TS typings. Ignoring until we have those. - '**/typescript-definition.spec.ts', - ], - - /** - * We can't run tests with `threads` on because we use `process.chdir()` in some tests and - * that isn't available in worker threads, and it's way too much work to mock out an entire - * filesystem and `fs` calls for the tests that use it. - * - * @see {@link https://github.com/vitest-dev/vitest/issues/566} - */ - pool: 'forks', + setupFiles: ['test/vitest.setup.ts'], }, }); diff --git a/vitest.config.ts b/vitest.config.mts similarity index 100% rename from vitest.config.ts rename to vitest.config.mts