docs: host multiple api versions

deletes defunct verisons of OpenAPI spec
This commit is contained in:
Christian Benincasa
2026-01-09 15:04:30 -05:00
parent de376a9f29
commit ff63052bc9
31 changed files with 121 additions and 48 deletions

View File

@@ -8,10 +8,21 @@
content="width=device-width, initial-scale=1" /> content="width=device-width, initial-scale=1" />
</head> </head>
<body> <body>
<div id="app"></div>
<!-- Need a Custom Header? Check out this example https://codepen.io/scalarorg/pen/VwOXqam --> <!-- Need a Custom Header? Check out this example https://codepen.io/scalarorg/pen/VwOXqam -->
<script <!-- <script
id="api-reference" id="api-reference"
data-url="/generated/tunarr-latest-openapi.json"></script> data-url="/generated/tunarr-latest-openapi.json"></script> -->
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script> <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
<script id="sources" src="/generated/openapi-specs.js"></script>
<script>
Scalar.createApiReference('#app', {
theme: "default",
favicon: 'assets/favicon.png',
hideClientButton: true,
sources,
})
</script>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,2 @@
const sources = [{"title":"Latest","slug":"latest","url":"/generated/tunarr-latest-openapi.json"},{"title":"1.0.16","slug":"1.0.16","url":"/generated/tunarr-v1.0.16-openapi.json"},{"title":"1.0.8","slug":"1.0.8","url":"/generated/tunarr-v1.0.8-openapi.json"},{"title":"1.0.3","slug":"1.0.3","url":"/generated/tunarr-v1.0.3-openapi.json"},{"title":"0.22.11","slug":"0.22.11","url":"/generated/tunarr-v0.22.11-openapi.json"}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,7 +13,8 @@
"lint-changed": "eslint --fix $(git diff --name-only HEAD -- './**/*.ts*' | xargs)", "lint-changed": "eslint --fix $(git diff --name-only HEAD -- './**/*.ts*' | xargs)",
"test": "turbo run test", "test": "turbo run test",
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"should-semantic-release": "should-semantic-release --verbose" "should-semantic-release": "should-semantic-release --verbose",
"generate-docs-script": "tsx scripts/generate-docs-script.ts"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.3.0", "@commitlint/cli": "^19.3.0",
@@ -24,6 +25,8 @@
"@release-it/bumper": "^7.0.5", "@release-it/bumper": "^7.0.5",
"@release-it/conventional-changelog": "^10.0.4", "@release-it/conventional-changelog": "^10.0.4",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@types/node": "22.10.7",
"@types/semver": "^7.7.1",
"@typescript-eslint/eslint-plugin": "^8.21.0", "@typescript-eslint/eslint-plugin": "^8.21.0",
"@typescript-eslint/parser": "^8.21.0", "@typescript-eslint/parser": "^8.21.0",
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.2.4",
@@ -42,7 +45,9 @@
"release-it": "^19.2.2", "release-it": "^19.2.2",
"release-it-pnpm": "^4.6.6", "release-it-pnpm": "^4.6.6",
"semantic-release": "^25.0.2", "semantic-release": "^25.0.2",
"semver": "^7.7.3",
"should-semantic-release": "^0.3.5", "should-semantic-release": "^0.3.5",
"tsx": "^4.20.5",
"turbo": "^2.5.3", "turbo": "^2.5.3",
"typescript": "catalog:", "typescript": "catalog:",
"vitest": "^3.2.4" "vitest": "^3.2.4"

36
pnpm-lock.yaml generated
View File

@@ -58,6 +58,12 @@ importers:
'@semantic-release/changelog': '@semantic-release/changelog':
specifier: ^6.0.3 specifier: ^6.0.3
version: 6.0.3(semantic-release@25.0.2(typescript@5.9.3)) version: 6.0.3(semantic-release@25.0.2(typescript@5.9.3))
'@types/node':
specifier: 22.10.7
version: 22.10.7
'@types/semver':
specifier: ^7.7.1
version: 7.7.1
'@typescript-eslint/eslint-plugin': '@typescript-eslint/eslint-plugin':
specifier: ^8.21.0 specifier: ^8.21.0
version: 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) version: 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
@@ -112,9 +118,15 @@ importers:
semantic-release: semantic-release:
specifier: ^25.0.2 specifier: ^25.0.2
version: 25.0.2(typescript@5.9.3) version: 25.0.2(typescript@5.9.3)
semver:
specifier: ^7.7.3
version: 7.7.3
should-semantic-release: should-semantic-release:
specifier: ^0.3.5 specifier: ^0.3.5
version: 0.3.5 version: 0.3.5
tsx:
specifier: ^4.20.5
version: 4.20.6
turbo: turbo:
specifier: ^2.5.3 specifier: ^2.5.3
version: 2.5.3 version: 2.5.3
@@ -3355,6 +3367,9 @@ packages:
'@types/semver@7.5.4': '@types/semver@7.5.4':
resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==} resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==}
'@types/semver@7.7.1':
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
'@types/split2@4.2.3': '@types/split2@4.2.3':
resolution: {integrity: sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==} resolution: {integrity: sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==}
@@ -8111,11 +8126,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
semver@7.6.3:
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
engines: {node: '>=10'}
hasBin: true
semver@7.7.2: semver@7.7.2:
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -11997,6 +12007,8 @@ snapshots:
'@types/semver@7.5.4': {} '@types/semver@7.5.4': {}
'@types/semver@7.7.1': {}
'@types/split2@4.2.3': '@types/split2@4.2.3':
dependencies: dependencies:
'@types/node': 22.10.7 '@types/node': 22.10.7
@@ -12041,7 +12053,7 @@ snapshots:
ignore: 5.2.4 ignore: 5.2.4
natural-compare: 1.4.0 natural-compare: 1.4.0
natural-compare-lite: 1.4.0 natural-compare-lite: 1.4.0
semver: 7.5.4 semver: 7.7.3
ts-api-utils: 1.0.3(typescript@5.9.3) ts-api-utils: 1.0.3(typescript@5.9.3)
optionalDependencies: optionalDependencies:
typescript: 5.9.3 typescript: 5.9.3
@@ -12271,7 +12283,7 @@ snapshots:
fast-glob: 3.3.3 fast-glob: 3.3.3
is-glob: 4.0.3 is-glob: 4.0.3
minimatch: 9.0.5 minimatch: 9.0.5
semver: 7.6.3 semver: 7.7.3
ts-api-utils: 2.0.0(typescript@5.9.3) ts-api-utils: 2.0.0(typescript@5.9.3)
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -14526,7 +14538,7 @@ snapshots:
process-warning: 5.0.0 process-warning: 5.0.0
rfdc: 1.4.1 rfdc: 1.4.1
secure-json-parse: 4.1.0 secure-json-parse: 4.1.0
semver: 7.7.2 semver: 7.7.3
toad-cache: 3.7.0 toad-cache: 3.7.0
fastq@1.17.1: fastq@1.17.1:
@@ -16246,7 +16258,7 @@ snapshots:
node-abi@3.78.0: node-abi@3.78.0:
dependencies: dependencies:
semver: 7.7.2 semver: 7.7.3
node-cache@5.1.2: node-cache@5.1.2:
dependencies: dependencies:
@@ -16296,7 +16308,7 @@ snapshots:
ignore-by-default: 1.0.1 ignore-by-default: 1.0.1
minimatch: 3.1.2 minimatch: 3.1.2
pstree.remy: 1.1.8 pstree.remy: 1.1.8
semver: 7.5.4 semver: 7.7.3
simple-update-notifier: 2.0.0 simple-update-notifier: 2.0.0
supports-color: 5.5.0 supports-color: 5.5.0
touch: 3.1.0 touch: 3.1.0
@@ -17163,7 +17175,7 @@ snapshots:
conventional-recommended-bump: 11.2.0 conventional-recommended-bump: 11.2.0
kolorist: 1.8.0 kolorist: 1.8.0
release-it: 19.2.2(@types/node@22.10.7)(magicast@0.3.5) release-it: 19.2.2(@types/node@22.10.7)(magicast@0.3.5)
semver: 7.7.2 semver: 7.7.3
transitivePeerDependencies: transitivePeerDependencies:
- magicast - magicast
@@ -17464,8 +17476,6 @@ snapshots:
dependencies: dependencies:
lru-cache: 6.0.0 lru-cache: 6.0.0
semver@7.6.3: {}
semver@7.7.2: {} semver@7.7.2: {}
semver@7.7.3: {} semver@7.7.3: {}

View File

@@ -0,0 +1,43 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import semver from 'semver';
function semverRegex() {
return /((0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)/gi;
}
const generatedDir = await fs.readdir('docs/generated');
const openApiSpecs = generatedDir
.filter((name) => name.endsWith('openapi.json') && !name.includes('latest'))
.sort((l, r) => {
const sv1 = semverRegex().exec(l.replace('-openapi.json', ''))?.[0] ?? l;
const sv2 = semverRegex().exec(r.replace('-openapi.json', ''))?.[0] ?? r;
return semver.rcompare(sv1, sv2);
});
// Generate the script...
const specs = [
{
title: 'Latest',
slug: 'latest',
url: '/generated/tunarr-latest-openapi.json',
},
...openApiSpecs.map((spec) => {
const v = semverRegex().exec(spec.replace('-openapi.json', ''))?.[0];
return {
title: v,
slug: v,
url: `/generated/${spec}`,
};
}),
];
const script = `
const sources = ${JSON.stringify(specs)}
`;
await fs.writeFile(
path.join(process.cwd(), 'docs', 'generated', 'openapi-specs.js'),
script,
);

View File

@@ -1,5 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
pnpm run generate-docs-script
docker build -f ./docker/docs.Dockerfile -t chrisbenincasa/tunarr-docs . docker build -f ./docker/docs.Dockerfile -t chrisbenincasa/tunarr-docs .
docker run --rm -it -p 8088:8000 -v "${PWD}":/docs chrisbenincasa/tunarr-docs docker run --rm -it -p 8088:8000 -v "${PWD}":/docs chrisbenincasa/tunarr-docs

32
tsconfig.json Normal file
View File

@@ -0,0 +1,32 @@
{
"compilerOptions": {
"module": "nodenext",
"target": "esnext",
"lib": [
"esnext"
],
"types": [
"node"
],
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"strict": true,
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true,
},
"include": [
"scripts/**/*.ts",
]
}