diff --git a/.github/package-lock.json b/.github/package-lock.json index 63cf40afaf2e..5d3be1ce0f99 100644 --- a/.github/package-lock.json +++ b/.github/package-lock.json @@ -24,6 +24,7 @@ "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", + "@types/semver": "^7.7.1", "@vitest/coverage-v8": "^3.2.4", "cross-env": "^10.1.0", "eslint": "^9.22.0", @@ -1036,6 +1037,7 @@ "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -1351,6 +1353,7 @@ "integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.2", @@ -1845,6 +1848,7 @@ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/ms": "*" } @@ -1874,7 +1878,8 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/ms": { "version": "2.1.0", @@ -1889,10 +1894,18 @@ "integrity": "sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.46.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", @@ -1939,6 +1952,7 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -2306,6 +2320,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2689,6 +2704,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3731,6 +3747,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -4210,6 +4227,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4302,6 +4320,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4377,6 +4396,7 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -4493,6 +4513,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4506,6 +4527,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", diff --git a/.github/package.json b/.github/package.json index 63477524f55d..3c85bf81241d 100644 --- a/.github/package.json +++ b/.github/package.json @@ -25,6 +25,7 @@ "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", + "@types/semver": "^7.7.1", "@vitest/coverage-v8": "^3.2.4", "cross-env": "^10.1.0", "eslint": "^9.22.0", diff --git a/.github/shared/cmd/spec-model.js b/.github/shared/cmd/spec-model.js index 3f6a690c260f..bcee4d62f664 100755 --- a/.github/shared/cmd/spec-model.js +++ b/.github/shared/cmd/spec-model.js @@ -5,7 +5,7 @@ import { SpecModel } from "../src/spec-model.js"; const USAGE = "Usage: npx spec-model path/to/spec [--debug] [--include-refs] [--relative-paths] [--no-embed-errors]\n" + - "Example: npx spec-model specification/contosowidgetmanager"; + "Example: npx spec-model specification/widget"; // Exclude first two args (node, script file) let args = process.argv.slice(2); @@ -42,7 +42,6 @@ const specModel = new SpecModel(specPath, { logger }); console.log( JSON.stringify( - // Always embed errors, since we always want to return a valid JSON object instead of throwing await specModel.toJSONAsync({ embedErrors: !noEmbedErrors, includeRefs, relativePaths }), null, 2, diff --git a/.github/shared/package-lock.json b/.github/shared/package-lock.json index 3d4295a1ea6e..28f6f4b7c2dc 100644 --- a/.github/shared/package-lock.json +++ b/.github/shared/package-lock.json @@ -22,6 +22,7 @@ "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", + "@types/semver": "^7.7.1", "@vitest/coverage-v8": "^3.2.4", "cross-env": "^10.1.0", "eslint": "^9.22.0", @@ -1247,6 +1248,7 @@ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/ms": "*" } @@ -1276,7 +1278,8 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/ms": { "version": "2.1.0", @@ -1291,10 +1294,18 @@ "integrity": "sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.46.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", @@ -1341,6 +1352,7 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -1708,6 +1720,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2070,6 +2083,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3047,6 +3061,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -3099,6 +3114,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -3629,6 +3645,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3684,6 +3701,7 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -3782,6 +3800,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", diff --git a/.github/shared/package.json b/.github/shared/package.json index c1be1d786a80..6784a847544c 100644 --- a/.github/shared/package.json +++ b/.github/shared/package.json @@ -47,6 +47,7 @@ "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", + "@types/semver": "^7.7.1", "@vitest/coverage-v8": "^3.2.4", "cross-env": "^10.1.0", "eslint": "^9.22.0", diff --git a/.github/shared/src/readme.js b/.github/shared/src/readme.js index a00d6cbb0755..f1fd14e30073 100644 --- a/.github/shared/src/readme.js +++ b/.github/shared/src/readme.js @@ -9,8 +9,17 @@ import { embedError } from "./spec-model.js"; import { Tag } from "./tag.js"; /** + * @typedef {import('./spec-model.js').ErrorJSON} ErrorJSON * @typedef {import('./spec-model.js').SpecModel} SpecModel * @typedef {import('./spec-model.js').ToJSONOptions} ToJSONOptions + * @typedef {import('./tag.js').TagJSON} TagJSON + */ + +/** + * @typedef {Object} ReadmeJSON + * @property {string} path + * @property {Object} globalConfig + * @property {(TagJSON|ErrorJSON)[]} tags */ /** @@ -43,7 +52,7 @@ export class Readme { */ #content; - /** @type {{globalConfig: Object, tags: Map} | undefined} */ + /** @type {{globalConfig: Record, tags: Map} | undefined} */ #data; /** @type {import('./logger.js').ILogger | undefined} */ @@ -210,7 +219,7 @@ export class Readme { } /** - * @returns {Promise} + * @returns {Promise>} */ async getGlobalConfig() { return (await this.#getData()).globalConfig; @@ -239,7 +248,7 @@ export class Readme { /** * @param {ToJSONOptions} [options] - * @returns {Promise} + * @returns {Promise} */ async toJSONAsync(options = {}) { const { relativePaths } = options; diff --git a/.github/shared/src/spec-model.js b/.github/shared/src/spec-model.js index d7335f1b3b33..e4614ac9879e 100644 --- a/.github/shared/src/spec-model.js +++ b/.github/shared/src/spec-model.js @@ -5,19 +5,33 @@ import { readme } from "./changed-files.js"; import { Readme } from "./readme.js"; import { SpecModelError } from "./spec-model-error.js"; -/** @type {Map} */ -const specModelCache = new Map(); +/** + * @typedef {import('./readme.js').ReadmeJSON} ReadmeJSON + * @typedef {import('./swagger.js').Swagger} Swagger + * @typedef {import('./tag.js').Tag} Tag + */ + +/** + * @typedef {Object} ErrorJSON + * @prop {string} error + */ + +/** + * @typedef {Object} SpecModelJSON + * @property {string} folder + * @property {(ReadmeJSON|ErrorJSON)[]} readmes + */ /** * @typedef {Object} ToJSONOptions * @prop {boolean} [embedErrors] * @prop {boolean} [includeRefs] * @prop {boolean} [relativePaths] - * - * @typedef {import('./swagger.js').Swagger} Swagger - * @typedef {import('./tag.js').Tag} Tag */ +/** @type {Map} */ +const specModelCache = new Map(); + export class SpecModel { /** @type {string} absolute path */ // @ts-expect-error Ignore error that value may not be set in ctor (since we may returned cached value) @@ -214,7 +228,7 @@ export class SpecModel { /** * @param {ToJSONOptions} [options] - * @returns {Promise} + * @returns {Promise} */ async toJSONAsync(options = {}) { return await embedError(async () => { @@ -242,7 +256,7 @@ export class SpecModel { * @param {() => Promise} fn * @param {Object} [options] * @param {boolean} [options.embedErrors] - * @returns {Promise} + * @returns {Promise} */ export async function embedError(fn, options = {}) { const { embedErrors } = options; diff --git a/.github/shared/src/swagger.js b/.github/shared/src/swagger.js index 064d4ad5bea4..af1da1517441 100644 --- a/.github/shared/src/swagger.js +++ b/.github/shared/src/swagger.js @@ -8,6 +8,7 @@ import { SpecModelError } from "./spec-model-error.js"; import { embedError } from "./spec-model.js"; /** + * @typedef {import('./spec-model.js').ErrorJSON} ErrorJSON * @typedef {import('./spec-model.js').Tag} Tag * @typedef {import('./spec-model.js').ToJSONOptions} ToJSONOptions */ @@ -19,6 +20,12 @@ import { embedError } from "./spec-model.js"; * @property {string} httpMethod - HTTP method (GET, POST, etc.) */ +/** + * @typedef {Object} SwaggerJSON + * @property {string} path + * @property {Object[]} [refs] + */ + /** * @type {import('@apidevtools/json-schema-ref-parser').ResolverOptions} */ @@ -200,7 +207,7 @@ export class Swagger { /** * @param {ToJSONOptions} [options] - * @returns {Promise} + * @returns {Promise} */ async toJSONAsync(options = {}) { const { includeRefs, relativePaths } = options; diff --git a/.github/shared/src/tag.js b/.github/shared/src/tag.js index feb331c56354..1a77a4c433e4 100644 --- a/.github/shared/src/tag.js +++ b/.github/shared/src/tag.js @@ -3,10 +3,18 @@ import { embedError } from "./spec-model.js"; import { Swagger } from "./swagger.js"; /** + * @typedef {import('./spec-model.js').ErrorJSON} ErrorJSON * @typedef {import('./readme.js').Readme} Readme + * @typedef {import('./swagger.js').SwaggerJSON} SwaggerJSON * @typedef {import('./spec-model.js').ToJSONOptions} ToJSONOptions */ +/** + * @typedef {Object} TagJSON + * @property {string} name + * @property {(SwaggerJSON|ErrorJSON)[]} inputFiles + */ + export class Tag { /** @type {Map} */ #inputFiles; @@ -68,7 +76,7 @@ export class Tag { /** * @param {ToJSONOptions} [options] - * @returns {Promise} + * @returns {Promise} */ async toJSONAsync(options = {}) { return await embedError( diff --git a/.github/shared/test/error-reporting.test.js b/.github/shared/test/error-reporting.test.js index 3045c6c3eb7f..756edfda111b 100644 --- a/.github/shared/test/error-reporting.test.js +++ b/.github/shared/test/error-reporting.test.js @@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { annotateFileError, setOutput, setSummary } from "../src/error-reporting.js"; describe("ErrorReporting", () => { + /** @type {import("vitest").MockInstance} */ let logSpy; beforeEach(() => { diff --git a/.github/shared/test/exec.test.js b/.github/shared/test/exec.test.js index bb773ea656d0..d8e951c0f314 100644 --- a/.github/shared/test/exec.test.js +++ b/.github/shared/test/exec.test.js @@ -39,7 +39,7 @@ describe("execFile", () => { describe("execNpm", () => { it("succeeds with --version", async () => { await expect(execNpm(["--version"], options)).resolves.toEqual({ - stdout: expect.toSatisfy((v) => semver.valid(v)), + stdout: expect.toSatisfy((v) => semver.valid(v) !== null), stderr: "", }); }); @@ -61,7 +61,7 @@ describe("execNpmExec", () => { // so it is used. it("runs prettier", async () => { await expect(execNpmExec(["prettier", "--version"], options)).resolves.toEqual({ - stdout: expect.toSatisfy((v) => semver.valid(v)), + stdout: expect.toSatisfy((v) => semver.valid(v) !== null), stderr: "", error: undefined, }); @@ -75,13 +75,15 @@ describe("isExecError", () => { const error = new Error(); expect(isExecError(error)).toBe(false); - error.stdout = "test"; - expect(isExecError(error)).toBe(true); + const execError = /** @type {import("../src/exec.js").ExecError} */ (error); - delete error.stdout; - expect(isExecError(error)).toBe(false); + execError.stdout = "test"; + expect(isExecError(execError)).toBe(true); + + delete execError.stdout; + expect(isExecError(execError)).toBe(false); - error.stderr = "test"; - expect(isExecError(error)).toBe(true); + execError.stderr = "test"; + expect(isExecError(execError)).toBe(true); }); }); diff --git a/.github/shared/test/git.test.js b/.github/shared/test/git.test.js index 6f26bd7db9f9..1e7e635d8ef5 100644 --- a/.github/shared/test/git.test.js +++ b/.github/shared/test/git.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { isFullGitSha } from "../src/git"; -import { fullGitSha } from "./examples"; +import { isFullGitSha } from "../src/git.js"; +import { fullGitSha } from "./examples.js"; describe("git", () => { it.each([ diff --git a/.github/shared/test/logger.test.js b/.github/shared/test/logger.test.js index fc7c6c9a368b..d855eb0b6d39 100644 --- a/.github/shared/test/logger.test.js +++ b/.github/shared/test/logger.test.js @@ -1,8 +1,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { ConsoleLogger, debugLogger, defaultLogger } from "../src/logger"; +import { ConsoleLogger, debugLogger, defaultLogger } from "../src/logger.js"; describe("logger", () => { - let debugSpy, errorSpy, logSpy, warnSpy; + /** @type {import("vitest").MockInstance} */ let debugSpy; + /** @type {import("vitest").MockInstance} */ let errorSpy; + /** @type {import("vitest").MockInstance} */ let logSpy; + /** @type {import("vitest").MockInstance} */ let warnSpy; beforeEach(() => { debugSpy = vi.spyOn(console, "debug"); diff --git a/.github/shared/test/set.test.js b/.github/shared/test/set.test.js index e48cc2acbd2d..93c637099420 100644 --- a/.github/shared/test/set.test.js +++ b/.github/shared/test/set.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { equals, intersect } from "../src/set"; +import { equals, intersect } from "../src/set.js"; describe("set", () => { it.each([ diff --git a/.github/shared/test/spec-model.test.js b/.github/shared/test/spec-model.test.js index 3dbbf859e365..5ca6416727c6 100644 --- a/.github/shared/test/spec-model.test.js +++ b/.github/shared/test/spec-model.test.js @@ -8,6 +8,13 @@ import { SpecModel } from "../src/spec-model.js"; import { Duration } from "../src/time.js"; import { repoRoot } from "./repo.js"; +/** + * @typedef {import('../src/readme.js').ReadmeJSON} ReadmeJSON + * @typedef {import('../src/spec-model.js').SpecModelJSON} SpecModelJSON + * @typedef {import('../src/swagger.js').SwaggerJSON} SwaggerJSON + * @typedef {import('../src/tag.js').TagJSON} TagJSON + */ + const options = { logger: new ConsoleLogger(/*debug*/ true) }; describe("SpecModel", () => { @@ -91,18 +98,28 @@ describe("SpecModel", () => { ); expect(inputFiles1[0].tag).toBe(tags[1]); - const jsonDefault = await specModel.toJSONAsync(); - const readmePathDefault = jsonDefault.readmes[0].path; + const jsonDefault = /** @type {SpecModelJSON} */ (await specModel.toJSONAsync()); + const readmeJSONDefault = /** @type {ReadmeJSON} */ (jsonDefault.readmes[0]); + const readmePathDefault = readmeJSONDefault.path; expect(isAbsolute(readmePathDefault)).toBe(true); - expect(jsonDefault.readmes[0].tags[0].inputFiles[0].refs).toBeUndefined(); - - const jsonRefsRelative = await specModel.toJSONAsync({ - includeRefs: true, - relativePaths: true, - }); - const readmePathRelative = jsonRefsRelative.readmes[0].path; + expect( + /** @type {SwaggerJSON} */ (/** @type {TagJSON} */ (readmeJSONDefault.tags[0]).inputFiles[0]) + .refs, + ).toBeUndefined(); + + const jsonRefsRelative = /** @type {SpecModelJSON} */ ( + await specModel.toJSONAsync({ + includeRefs: true, + relativePaths: true, + }) + ); + const readmeJSONRelative = /** @type {ReadmeJSON} */ (jsonRefsRelative.readmes[0]); + const readmePathRelative = readmeJSONRelative.path; expect(isAbsolute(readmePathRelative)).toBe(false); - expect(jsonRefsRelative.readmes[0].tags[0].inputFiles[0].refs).toBeDefined(); + expect( + /** @type {SwaggerJSON}*/ (/** @type {TagJSON} */ (readmeJSONRelative.tags[0]).inputFiles[0]) + .refs, + ).toBeDefined(); }); it("uses strings for tag names and doesn't parse Date object", async () => { @@ -380,6 +397,7 @@ describe.skip("Parse readmes", () => { "runs properly against specific services", { timeout: 30 * Duration.Minute }, async ({ expect }) => { + /** @type {string[]} */ const folders = [ // Fill in services to test here ]; diff --git a/.github/shared/tsconfig.json b/.github/shared/tsconfig.json index fd8d4c43ea86..49aa7c7cbf78 100644 --- a/.github/shared/tsconfig.json +++ b/.github/shared/tsconfig.json @@ -8,8 +8,4 @@ "incremental": false, "noEmit": true, }, - "include": [ - // Only check runtime sources. Tests currently have too many errors. - "**/src/**/*.js", - ], } diff --git a/.github/shared/vitest.config.js b/.github/shared/vitest.config.js index d9fa13bfbfba..c1d6d511c86c 100644 --- a/.github/shared/vitest.config.js +++ b/.github/shared/vitest.config.js @@ -4,13 +4,14 @@ export default defineConfig({ esbuild: { // Ignore tsconfig.json, since it's only used for type checking, and causes // a warning if vitest tries to load it + // @ts-expect-error: tsConfig' does not exist in type 'ESBuildOptions' tsConfig: false, }, test: { coverage: { exclude: [ - ...configDefaults.coverage.exclude, + ...(configDefaults.coverage.exclude ?? []), // Not worth testing CLI code "cmd/**/*.js", diff --git a/package-lock.json b/package-lock.json index a37b3e6e2ad8..1aded5da3050 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,6 +64,7 @@ "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", + "@types/semver": "^7.7.1", "@vitest/coverage-v8": "^3.2.4", "cross-env": "^10.1.0", "eslint": "^9.22.0", @@ -683,6 +684,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -1037,6 +1039,7 @@ "integrity": "sha512-sqOYBUghAtVMBiAWwT3fMRVSDNwR7IU3AQ96n/ErhAthwWjTe7PFVfK/MPjpI1mO3cdyLeS2DGyI3gt/waWP4g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -1063,6 +1066,7 @@ "integrity": "sha512-+BVZf1OfSZfEaXsOp1ZUCyjjZjC465ieIAr1zEz4yD7KaWGW+yE/w+HXbsRNNw0B52FhgVcWX6iVuvBtMZpNMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "change-case": "~5.4.4", "pluralize": "^8.0.0" @@ -1131,6 +1135,7 @@ "integrity": "sha512-CUhw8vkMzDTYlRfddQERT0ZEJAtwjKteRNuhs1S3IrDceJlHEiXTK+FpPwen/F5DBbj1w2SgnSFfaJ3fL171/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "change-case": "~5.4.4", "pluralize": "^8.0.0", @@ -3521,6 +3526,7 @@ "integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.2", @@ -4227,6 +4233,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4317,6 +4324,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4657,7 +4665,8 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/@ts-common/property-set": { "version": "0.1.0", @@ -4867,7 +4876,8 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/lodash": { "version": "4.17.20", @@ -4911,6 +4921,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", @@ -4988,6 +5005,7 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -5219,6 +5237,7 @@ "integrity": "sha512-REJgZOEZ9g9CC72GGT0+nLbjW+5WVlCfm1d6w18N5RsUo7vLXs8IPXwq7xZJzoqU99Q9B4keqzPuTU4OrDUTrA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "~7.27.1", "@inquirer/prompts": "^7.4.0", @@ -5375,6 +5394,7 @@ "integrity": "sha512-V7unXnj+sZoa/1wQG8G6x2TiQqotx18S/qFbDzdfJRPCVpH/Z3xIpppce4jTZALXT97tKZK5GDHijn2zWuWWxg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -5388,6 +5408,7 @@ "integrity": "sha512-52XLXwqSY2SY6nSvfkiTsNiJzlMeIAZ6MFIVJ5YkoibA21TNAP4DtjTZgC2GieZLY2NNN/rqDCqBX+DyWqTrfQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -5407,6 +5428,7 @@ "integrity": "sha512-27sXkSK2r1sAmVMLv+pwlN/Cm+yg9nEK8iuGyJRuEkBk7hcsJDbTnBlsEvlRTI8DqljtzA7YECDHBLK88zZHeg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -5468,6 +5490,7 @@ "integrity": "sha512-rQ+RP0kcrKWjbpCIkBd8hpxYSNc3CfQxl0MLP1+MYGRHlHL8ss4xbwdANIYZXZZ2i2Hqt19B7cEUGD4MLoCHvw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -5482,6 +5505,7 @@ "integrity": "sha512-8iODUY3C/0hR9sTzyHeTgYfZkKeqZM+/P0OmN1ZWlLUokXQ67yydGXIqnjl+yaeuntwN8H2DDwLguU15c+j+UQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -5498,6 +5522,7 @@ "integrity": "sha512-ubvxCN+SZwN9aEarz8CPtMJgnopeu8dXyut47q0FAPp9nykmXy7s+dmsopR+7OX0Fhcnh8ZFYTQcJzJ3QftOiQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -5526,6 +5551,7 @@ "integrity": "sha512-wdLcVx5UW4WRks/OXfqLiaDTtWfAWgv/nj69u99gRJU6iY9ExEvK5x9NQszZQKYnu6tM7nkoYMg4zu+7YBUBaw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -5539,6 +5565,7 @@ "integrity": "sha512-JVafN1nZE3BcQrKbaAFVWw/IleTRdsJpwT3oZ2m7EfWnG30sKtoR9inF9dRoW+XXIjNzCfeYqjkwzEkEnIrCww==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" }, @@ -5714,6 +5741,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7001,6 +7029,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8887,6 +8916,7 @@ "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 10.16.0" } @@ -9813,7 +9843,8 @@ "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/optionator": { "version": "0.9.4", @@ -10106,6 +10137,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -10271,7 +10303,8 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", @@ -11364,6 +11397,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11483,7 +11517,8 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tunnel": { "version": "0.0.6", @@ -11592,6 +11627,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11754,6 +11790,7 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -11870,6 +11907,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11883,6 +11921,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4",