mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-20 16:54:17 +00:00
* docs(admin): design for typesafe API client + TanStack Query rails (#7638) Rails-only scope: codegen toolchain, runtime client, and provider — no call-site migrations. Admin endpoints are not yet covered by the OpenAPI spec, so a separate issue will follow before any migration is useful. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(admin): implementation plan for typesafe API client rails (#7638) Step-by-step task breakdown for the rails-only PR: codegen toolchain, runtime client, TanStack Query provider, CI freshness check, docs. No call-site migrations until admin endpoints are added to the OpenAPI spec (separate follow-up). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(api): export generateDefinitionForVersion from openapi hook Required by the admin codegen script (#7638) to dump the OpenAPI spec without booting Express. No behavior change for the request hook. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(admin): add OpenAPI codegen + TanStack Query deps (#7638) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(admin): add OpenAPI spec dump entry (#7638) Loaded via tsx by gen-api.mjs in the next commit. Writes JSON to a file path argument so log4js stdout output (from Settings init) does not pollute the spec output. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(admin): wire OpenAPI codegen into build (#7638) Adds gen:api script and amends build/build-copy to regenerate admin/src/api/schema.d.ts before compiling. The generated file is checked in so it shows up in PR review and so a fresh checkout doesn't need codegen to typecheck. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(admin): typed openapi-fetch + react-query client (#7638) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(admin): TanStack Query provider, dev-only devtools (#7638) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(admin): mount TanStack Query provider at root (#7638) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(admin): smoke test for typed openapi-fetch client (#7638) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(admin): verify generated OpenAPI schema is up to date (#7638) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(admin): document OpenAPI codegen workflow (#7638) Replaces the default Vite scaffold README with admin-specific scripts table and codegen workflow notes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * build(admin): exclude __tests__ from tsc include (#7638) The smoke test imports node:test/node:assert which need @types/node. Admin source is browser-only, so excluding __tests__ from the production typecheck is cleaner than adding Node types to the bundle config. The test still runs under tsx, which doesn't share this constraint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: regenerate lockfile with pnpm 10 to restore overrides block (#7638) Adding admin deps with pnpm 11 stripped the top-level \`overrides:\` section from pnpm-lock.yaml, which CI uses pnpm 10 to verify. Result: ERR_PNPM_LOCKFILE_CONFIG_MISMATCH on every job. Re-running pnpm 10 \`install --lockfile-only\` restores the overrides block; the new admin package entries land in the same commit. Two stale lockfile entries not present in package.json (\`serialize-javascript\` version pin and \`uuid@<14.0.0\`) were normalized by the regen — package.json is the source of truth for those. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * build(docker): preserve strictDepBuilds=false in trimmed workspace yaml (#7638) Adding tsx as an admin devDep brings esbuild@0.27.x into the resolved subgraph, and the Dockerfile's runtime stage was overwriting pnpm-workspace.yaml with a stripped-down version that lost the strictDepBuilds=false setting from the source repo. With pnpm 10's default of strictDepBuilds=true, the install then errors on ERR_PNPM_IGNORED_BUILDS for esbuild + scarf rather than warning. Restore the strictDepBuilds=false and the @scarf/scarf ignore in the trimmed yaml so the production install matches develop's behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(admin): point client baseUrl at /api/<version> via codegen (#7638) Qodo flagged: with baseUrl='/' and schema paths like '/createGroup', calls landed at /createGroup, but the backend mounts the FLAT-style spec under /api/<version>/. So once a call site lands, every request 404s. gen:api now also emits admin/src/api/version.ts containing LATEST_API_VERSION (read from info.version in the spec) and a derived API_BASE_URL = `/api/<version>`. client.ts imports API_BASE_URL. Workflow freshness check covers both generated files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * build(admin): cross-platform spawn in gen-api.mjs (#7638) Windows CI failed because spawnSync('pnpm', ...) cannot resolve pnpm.cmd without a shell. Set shell:true on win32 only so Linux/macOS runs avoid Node's DEP0190 warning. All spawn args are literal strings, so the shell variant is not an injection risk. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bcf5c91c15
commit
b97baa3ef5
16 changed files with 5081 additions and 49 deletions
10
.github/workflows/frontend-admin-tests.yml
vendored
10
.github/workflows/frontend-admin-tests.yml
vendored
|
|
@ -68,6 +68,16 @@ jobs:
|
|||
name: Disable import/export rate limiting
|
||||
run: |
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 100000000/' -i settings.json
|
||||
- name: Verify admin OpenAPI schema is up to date
|
||||
working-directory: admin
|
||||
run: |
|
||||
pnpm gen:api
|
||||
if ! git diff --exit-code src/api/schema.d.ts src/api/version.ts; then
|
||||
echo ""
|
||||
echo "::error::admin/src/api/schema.d.ts or version.ts is out of date."
|
||||
echo "Run \`pnpm --filter admin gen:api\` and commit the result."
|
||||
exit 1
|
||||
fi
|
||||
- name: Build admin frontend
|
||||
working-directory: admin
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -1,30 +1,67 @@
|
|||
# React + TypeScript + Vite
|
||||
# Admin UI
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
Vite + React 19 single-page app served at `/admin`. Talks to the backend over
|
||||
socket.io for the existing settings / plugins / pads pages, and (when
|
||||
endpoints are added to the OpenAPI spec) over a typed REST client.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
## Scripts
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
| Script | What it does |
|
||||
| -------------------- | -------------------------------------------------------- |
|
||||
| `pnpm dev` | Vite dev server. Expects an etherpad backend on :9001. |
|
||||
| `pnpm gen:api` | Regenerates `src/api/schema.d.ts` from the OpenAPI spec. |
|
||||
| `pnpm build` | `gen:api` + `tsc` + `vite build`. |
|
||||
| `pnpm build-copy` | Same, but writes into `../src/templates/admin`. |
|
||||
| `pnpm test` | Smoke tests for the API client wiring. |
|
||||
| `pnpm lint` | ESLint. |
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
## Typed API client
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
The admin uses [`openapi-typescript`] to generate types from
|
||||
`src/node/hooks/express/openapi.ts`, [`openapi-fetch`] for typed requests, and
|
||||
[`openapi-react-query`] for TanStack Query bindings.
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
[`openapi-typescript`]: https://github.com/openapi-ts/openapi-typescript
|
||||
[`openapi-fetch`]: https://github.com/openapi-ts/openapi-typescript/tree/main/packages/openapi-fetch
|
||||
[`openapi-react-query`]: https://github.com/openapi-ts/openapi-typescript/tree/main/packages/openapi-react-query
|
||||
|
||||
```js
|
||||
export default {
|
||||
// other rules...
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
}
|
||||
### Regenerating the schema
|
||||
|
||||
```sh
|
||||
pnpm --filter admin gen:api
|
||||
```
|
||||
|
||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
||||
This runs `admin/scripts/gen-api.mjs`, which loads
|
||||
`src/node/hooks/express/openapi.ts`, calls `generateDefinitionForVersion` for
|
||||
the latest API version, pipes the JSON through `openapi-typescript`, and
|
||||
writes the result to `admin/src/api/schema.d.ts`. The latest API version
|
||||
read from the spec is also emitted to `admin/src/api/version.ts` so
|
||||
`client.ts` can build the right `/api/<version>/` baseUrl. Both generated
|
||||
files are checked in.
|
||||
|
||||
Run `gen:api` after any change to:
|
||||
|
||||
- `src/node/hooks/express/openapi.ts`
|
||||
- `src/node/handler/APIHandler.ts` (changes to `latestApiVersion`)
|
||||
- the resource definitions referenced by `openapi.ts`
|
||||
|
||||
### CI freshness check
|
||||
|
||||
`.github/workflows/frontend-admin-tests.yml` runs `pnpm gen:api` and fails the
|
||||
build if `admin/src/api/schema.d.ts` is out of date. If you see the failure
|
||||
locally, run `pnpm --filter admin gen:api` and commit the regenerated file.
|
||||
|
||||
### Using the client
|
||||
|
||||
```tsx
|
||||
import { $api } from './api/client';
|
||||
|
||||
const SettingsPanel = () => {
|
||||
const { data } = $api.useQuery('get', '/admin/settings'); // example
|
||||
return <pre>{JSON.stringify(data, null, 2)}</pre>;
|
||||
};
|
||||
```
|
||||
|
||||
The admin endpoints are not yet present in the OpenAPI spec — this client is
|
||||
in place to support upcoming work (see issue #7638 follow-up). For now, it is
|
||||
exercised only by the smoke test.
|
||||
|
|
|
|||
|
|
@ -5,13 +5,19 @@
|
|||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"gen:api": "node scripts/gen-api.mjs",
|
||||
"build": "pnpm gen:api && tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"build-copy": "tsc && vite build --outDir ../src/templates/admin --emptyOutDir",
|
||||
"preview": "vite preview"
|
||||
"build-copy": "pnpm gen:api && tsc && vite build --outDir ../src/templates/admin --emptyOutDir",
|
||||
"preview": "vite preview",
|
||||
"test": "tsx --test src/api/__tests__/client.test.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-switch": "^1.2.6"
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@tanstack/react-query": "^5.100.9",
|
||||
"@tanstack/react-query-devtools": "^5.100.9",
|
||||
"openapi-fetch": "^0.17.0",
|
||||
"openapi-react-query": "^0.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
|
|
@ -28,12 +34,14 @@
|
|||
"i18next": "^26.0.9",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"lucide-react": "^1.14.0",
|
||||
"openapi-typescript": "^7.13.0",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"react-hook-form": "^7.75.0",
|
||||
"react-i18next": "^17.0.6",
|
||||
"react-router-dom": "^7.15.0",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.0.10",
|
||||
"vite-plugin-babel": "^1.6.0",
|
||||
|
|
|
|||
46
admin/scripts/dump-spec.ts
Normal file
46
admin/scripts/dump-spec.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// admin/scripts/dump-spec.ts
|
||||
//
|
||||
// Imports the OpenAPI spec builder from the etherpad source and writes the
|
||||
// flat-style spec for the latest API version as JSON to the file path passed
|
||||
// as argv[2]. Invoked by admin/scripts/gen-api.mjs via `tsx`.
|
||||
//
|
||||
// Why a file argument instead of stdout: importing `openapi.ts` triggers
|
||||
// `Settings` init, which configures log4js to write INFO/WARN lines to
|
||||
// stdout. Capturing stdout would mix logs with JSON.
|
||||
|
||||
import { writeFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
const outFile = process.argv[2];
|
||||
if (!outFile) {
|
||||
process.stderr.write('Usage: tsx scripts/dump-spec.ts <output-path>\n');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(here, '..', '..');
|
||||
|
||||
const apiHandlerPath = path.join(repoRoot, 'src', 'node', 'handler', 'APIHandler.ts');
|
||||
const openapiPath = path.join(repoRoot, 'src', 'node', 'hooks', 'express', 'openapi.ts');
|
||||
|
||||
// `openapi.ts` and `APIHandler.ts` use CommonJS-style `exports.*`. Under tsx's
|
||||
// ESM dynamic import, the whole `module.exports` is exposed as `default`.
|
||||
type ApiHandlerModule = { latestApiVersion: string };
|
||||
type OpenApiModule = {
|
||||
generateDefinitionForVersion: (version: string, style?: string) => unknown;
|
||||
APIPathStyle: { FLAT: string; REST: string };
|
||||
};
|
||||
|
||||
const apiHandlerMod = await import(pathToFileURL(apiHandlerPath).href);
|
||||
const openapiMod = await import(pathToFileURL(openapiPath).href);
|
||||
|
||||
const apiHandler = (apiHandlerMod.default ?? apiHandlerMod) as ApiHandlerModule;
|
||||
const openapi = (openapiMod.default ?? openapiMod) as OpenApiModule;
|
||||
|
||||
const spec = openapi.generateDefinitionForVersion(
|
||||
apiHandler.latestApiVersion,
|
||||
openapi.APIPathStyle.FLAT,
|
||||
);
|
||||
|
||||
writeFileSync(path.resolve(outFile), JSON.stringify(spec, null, 2), 'utf8');
|
||||
78
admin/scripts/gen-api.mjs
Normal file
78
admin/scripts/gen-api.mjs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// admin/scripts/gen-api.mjs
|
||||
//
|
||||
// Regenerates admin/src/api/schema.d.ts from the live OpenAPI spec exported
|
||||
// by src/node/hooks/express/openapi.ts. Run via `pnpm --filter admin gen:api`.
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdtempSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const adminRoot = path.resolve(here, '..');
|
||||
const outFile = path.join(adminRoot, 'src', 'api', 'schema.d.ts');
|
||||
|
||||
const tmpDir = mkdtempSync(path.join(tmpdir(), 'etherpad-openapi-'));
|
||||
const specPath = path.join(tmpDir, 'spec.json');
|
||||
|
||||
// On Windows pnpm resolves to pnpm.cmd, which spawnSync can only find via a
|
||||
// shell. Use shell on Windows only to avoid Node's DEP0190 warning elsewhere.
|
||||
// Every argument here is fixed (no user input) so the shell:true variant is
|
||||
// not an injection risk.
|
||||
const spawnOpts = {
|
||||
cwd: adminRoot,
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32',
|
||||
};
|
||||
|
||||
try {
|
||||
const dump = spawnSync(
|
||||
'pnpm',
|
||||
['exec', 'tsx', 'scripts/dump-spec.ts', specPath],
|
||||
spawnOpts,
|
||||
);
|
||||
if (dump.status !== 0) {
|
||||
console.error(`dump-spec.ts failed with exit code ${dump.status}`);
|
||||
process.exit(dump.status ?? 1);
|
||||
}
|
||||
|
||||
const gen = spawnSync(
|
||||
'pnpm',
|
||||
['exec', 'openapi-typescript', specPath, '-o', outFile],
|
||||
spawnOpts,
|
||||
);
|
||||
if (gen.status !== 0) {
|
||||
console.error(`openapi-typescript failed with exit code ${gen.status}`);
|
||||
process.exit(gen.status ?? 1);
|
||||
}
|
||||
|
||||
const header =
|
||||
`// GENERATED — do not edit. Run \`pnpm --filter admin gen:api\` to regenerate.\n` +
|
||||
`// Source: src/node/hooks/express/openapi.ts (#7638)\n\n`;
|
||||
const body = readFileSync(outFile, 'utf8');
|
||||
writeFileSync(outFile, header + body, 'utf8');
|
||||
|
||||
// Emit a runtime-side version constant so client.ts can build the right
|
||||
// baseUrl. Generated paths are unprefixed (e.g. "/createGroup"), but the
|
||||
// backend mounts the FLAT-style spec under /api/<version>/.
|
||||
const spec = JSON.parse(readFileSync(specPath, 'utf8'));
|
||||
const apiVersion = spec?.info?.version;
|
||||
if (typeof apiVersion !== 'string' || apiVersion.length === 0) {
|
||||
console.error('OpenAPI spec is missing info.version; cannot emit version.ts');
|
||||
process.exit(1);
|
||||
}
|
||||
const versionFile = path.join(adminRoot, 'src', 'api', 'version.ts');
|
||||
writeFileSync(
|
||||
versionFile,
|
||||
header +
|
||||
`export const LATEST_API_VERSION = ${JSON.stringify(apiVersion)};\n` +
|
||||
`export const API_BASE_URL = \`/api/\${LATEST_API_VERSION}\`;\n`,
|
||||
'utf8',
|
||||
);
|
||||
|
||||
console.log(`Wrote ${path.relative(process.cwd(), outFile)}`);
|
||||
console.log(`Wrote ${path.relative(process.cwd(), versionFile)}`);
|
||||
} finally {
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
40
admin/src/api/QueryProvider.tsx
Normal file
40
admin/src/api/QueryProvider.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// admin/src/api/QueryProvider.tsx
|
||||
//
|
||||
// TanStack Query provider for the admin UI. Devtools are loaded lazily and
|
||||
// only in dev builds so they don't ship to production.
|
||||
|
||||
import { lazy, Suspense, useState, type ReactNode } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
const Devtools = import.meta.env.DEV
|
||||
? lazy(() =>
|
||||
import('@tanstack/react-query-devtools').then((m) => ({
|
||||
default: m.ReactQueryDevtools,
|
||||
})),
|
||||
)
|
||||
: null;
|
||||
|
||||
export const QueryProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [client] = useState(
|
||||
() =>
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 30_000,
|
||||
refetchOnWindowFocus: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={client}>
|
||||
{children}
|
||||
{Devtools && (
|
||||
<Suspense fallback={null}>
|
||||
<Devtools initialIsOpen={false} />
|
||||
</Suspense>
|
||||
)}
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
16
admin/src/api/__tests__/client.test.ts
Normal file
16
admin/src/api/__tests__/client.test.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// admin/src/api/__tests__/client.test.ts
|
||||
//
|
||||
// Smoke test that the OpenAPI client module loads and exposes the expected
|
||||
// surface. Catches toolchain wiring regressions (missing peer deps,
|
||||
// generator output that doesn't export `paths`, etc.).
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
test('client module exports fetchClient and $api', async () => {
|
||||
const mod = await import('../client.ts');
|
||||
assert.ok(mod.fetchClient, 'fetchClient export is present');
|
||||
assert.ok(mod.$api, '$api export is present');
|
||||
assert.equal(typeof mod.fetchClient.GET, 'function', 'fetchClient.GET is a function');
|
||||
assert.equal(typeof mod.$api.useQuery, 'function', '$api.useQuery is a function');
|
||||
});
|
||||
12
admin/src/api/client.ts
Normal file
12
admin/src/api/client.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// admin/src/api/client.ts
|
||||
//
|
||||
// Typed HTTP client and TanStack Query hooks derived from the generated
|
||||
// OpenAPI schema. Regenerate the schema with `pnpm --filter admin gen:api`.
|
||||
|
||||
import createClient from 'openapi-fetch';
|
||||
import createQueryHooks from 'openapi-react-query';
|
||||
import type { paths } from './schema';
|
||||
import { API_BASE_URL } from './version';
|
||||
|
||||
export const fetchClient = createClient<paths>({ baseUrl: API_BASE_URL });
|
||||
export const $api = createQueryHooks(fetchClient);
|
||||
3567
admin/src/api/schema.d.ts
vendored
Normal file
3567
admin/src/api/schema.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
5
admin/src/api/version.ts
Normal file
5
admin/src/api/version.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// GENERATED — do not edit. Run `pnpm --filter admin gen:api` to regenerate.
|
||||
// Source: src/node/hooks/express/openapi.ts (#7638)
|
||||
|
||||
export const LATEST_API_VERSION = "1.3.1";
|
||||
export const API_BASE_URL = `/api/${LATEST_API_VERSION}`;
|
||||
|
|
@ -14,6 +14,7 @@ import {PadPage} from "./pages/PadPage.tsx";
|
|||
import {ToastDialog} from "./utils/Toast.tsx";
|
||||
import {ShoutPage} from "./pages/ShoutPage.tsx";
|
||||
import {UpdatePage} from "./pages/UpdatePage.tsx";
|
||||
import {QueryProvider} from './api/QueryProvider.tsx';
|
||||
|
||||
const router = createBrowserRouter(createRoutesFromElements(
|
||||
<><Route element={<App/>}>
|
||||
|
|
@ -34,11 +35,13 @@ const router = createBrowserRouter(createRoutesFromElements(
|
|||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Toast.Provider>
|
||||
<ToastDialog/>
|
||||
<RouterProvider router={router}/>
|
||||
</Toast.Provider>
|
||||
</I18nextProvider>
|
||||
<QueryProvider>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Toast.Provider>
|
||||
<ToastDialog/>
|
||||
<RouterProvider router={router}/>
|
||||
</Toast.Provider>
|
||||
</I18nextProvider>
|
||||
</QueryProvider>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,5 +21,6 @@
|
|||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["src/**/__tests__/**"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,804 @@
|
|||
# Issue 7638 — Typesafe Admin API Client + TanStack Query Rails Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Lay down the codegen toolchain, runtime client, and TanStack Query provider for the admin UI. No call-site migrations.
|
||||
|
||||
**Architecture:** A small Node script imports the OpenAPI spec builder from `src/node/hooks/express/openapi.ts`, writes the JSON to a temp file, and runs `openapi-typescript` to produce a checked-in `admin/src/api/schema.d.ts`. The runtime exposes a typed `openapi-fetch` client and `openapi-react-query` hooks via `admin/src/api/client.ts`, mounted under a `<QueryProvider>` at the admin root. CI re-runs codegen and fails if the working tree is dirty.
|
||||
|
||||
**Tech Stack:** TypeScript, React 19, Vite (rolldown-vite), `openapi-typescript`, `openapi-fetch`, `openapi-react-query`, `@tanstack/react-query`, `@tanstack/react-query-devtools`, `tsx` (devDep, runs the codegen script against TS source).
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-05-01-issue-7638-admin-typesafe-api-design.md`
|
||||
|
||||
**Branch:** `chore/admin-typesafe-api-7638` (already cut off `origin/develop`, design doc committed as `41d2babf4`).
|
||||
|
||||
**Working directory for all commands:** `/home/jose/etherpad/etherpad-lite` unless otherwise stated.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
**Create:**
|
||||
- `admin/scripts/gen-api.mjs` — orchestrator script. Invokes `tsx` to run a small TS entry that prints the spec JSON, captures stdout to a temp file, then shells out to `openapi-typescript`.
|
||||
- `admin/scripts/dump-spec.ts` — TS entry that imports `generateDefinitionForVersion` from the etherpad source and writes the JSON to stdout.
|
||||
- `admin/src/api/schema.d.ts` — generated. Checked in.
|
||||
- `admin/src/api/client.ts` — `openapi-fetch` + `openapi-react-query` instances.
|
||||
- `admin/src/api/QueryProvider.tsx` — TanStack Query provider, dev-only devtools.
|
||||
- `admin/src/api/__tests__/client.test.ts` — module-load smoke test.
|
||||
- `admin/README.md` — codegen docs (file does not currently exist).
|
||||
|
||||
**Modify:**
|
||||
- `src/node/hooks/express/openapi.ts` — add `export { generateDefinitionForVersion }` at the end so external scripts can call the spec builder. Surgical change, no behavior delta.
|
||||
- `admin/package.json` — add deps and `gen:api` script; amend `build` to run `gen:api` first.
|
||||
- `admin/src/main.tsx` — wrap router subtree in `<QueryProvider>`.
|
||||
- `.github/workflows/frontend-admin-tests.yml` — add a freshness-check step before the existing admin build step.
|
||||
|
||||
**Conventions to honor:**
|
||||
- Per project memory, the PR will go to `johnmclear/etherpad-lite`, not `ether/etherpad-lite`.
|
||||
- Commit at the end of each task.
|
||||
- Run `pnpm ts-check` and admin's lint at the end before declaring done.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Export the spec builder from `openapi.ts`
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/node/hooks/express/openapi.ts:422` (and end of file)
|
||||
|
||||
The script needs to call `generateDefinitionForVersion` from outside the module. It is currently only used within the file. Adding a CommonJS-style export keeps the existing `exports.expressPreSession` style consistent.
|
||||
|
||||
- [ ] **Step 1: Read the current export style at the bottom of the file**
|
||||
|
||||
Run: `grep -n "^exports\." src/node/hooks/express/openapi.ts`
|
||||
Expected output: a line like `578:exports.expressPreSession = async (hookName:string, {app}:any) => {`
|
||||
|
||||
- [ ] **Step 2: Add the export**
|
||||
|
||||
Append at the end of `src/node/hooks/express/openapi.ts` (after the existing hook export, after line 771):
|
||||
|
||||
```ts
|
||||
exports.generateDefinitionForVersion = generateDefinitionForVersion;
|
||||
exports.APIPathStyle = APIPathStyle;
|
||||
```
|
||||
|
||||
(Both are needed: the script will call `generateDefinitionForVersion(apiHandler.latestApiVersion, APIPathStyle.FLAT)` and we want a single import surface.)
|
||||
|
||||
- [ ] **Step 3: Verify ts-check still passes**
|
||||
|
||||
Run: `pnpm ts-check`
|
||||
Expected: no new errors. (If pre-existing errors are present, confirm none are in `openapi.ts`.)
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/node/hooks/express/openapi.ts
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat(api): export generateDefinitionForVersion from openapi hook
|
||||
|
||||
Required by the admin codegen script (#7638) to dump the OpenAPI spec
|
||||
without booting Express. No behavior change for the request hook.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Add admin dependencies
|
||||
|
||||
**Files:**
|
||||
- Modify: `admin/package.json`
|
||||
|
||||
- [ ] **Step 1: Read the current `admin/package.json`**
|
||||
|
||||
Run: `cat admin/package.json`
|
||||
Expected: confirm there is a `dependencies` block and a `devDependencies` block.
|
||||
|
||||
- [ ] **Step 2: Install runtime deps**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
pnpm --filter admin add @tanstack/react-query @tanstack/react-query-devtools openapi-fetch openapi-react-query
|
||||
```
|
||||
Expected: deps added under `dependencies`. `pnpm-lock.yaml` updated at repo root.
|
||||
|
||||
- [ ] **Step 3: Install dev deps**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
pnpm --filter admin add -D openapi-typescript tsx
|
||||
```
|
||||
Expected: deps added under `devDependencies`.
|
||||
|
||||
- [ ] **Step 4: Sanity check the diff**
|
||||
|
||||
Run: `git diff admin/package.json`
|
||||
Expected: six new entries (4 deps, 2 devDeps), no other changes.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add admin/package.json pnpm-lock.yaml
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore(admin): add OpenAPI codegen + TanStack Query deps (#7638)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Write the spec-dump entry
|
||||
|
||||
**Files:**
|
||||
- Create: `admin/scripts/dump-spec.ts`
|
||||
|
||||
This file is intentionally tiny. It runs under `tsx` so it can resolve the etherpad-lite TypeScript source directly.
|
||||
|
||||
- [ ] **Step 1: Create the file**
|
||||
|
||||
```ts
|
||||
// admin/scripts/dump-spec.ts
|
||||
//
|
||||
// Imports the OpenAPI spec builder from the etherpad source and writes the
|
||||
// flat-style spec for the latest API version as JSON to stdout. Invoked by
|
||||
// admin/scripts/gen-api.mjs via `tsx`.
|
||||
|
||||
import path from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
const repoRoot = path.resolve(__dirname, '..', '..');
|
||||
|
||||
// `openapi.ts` uses CommonJS-style `exports.*` despite living in an ESM repo,
|
||||
// so we go through createRequire to load it cleanly.
|
||||
import { createRequire } from 'node:module';
|
||||
const require = createRequire(pathToFileURL(path.join(repoRoot, 'src', 'node', 'hooks', 'express', 'openapi.ts')).toString());
|
||||
|
||||
const apiHandler = require('../../src/node/handler/APIHandler');
|
||||
const { generateDefinitionForVersion, APIPathStyle } =
|
||||
require('../../src/node/hooks/express/openapi') as {
|
||||
generateDefinitionForVersion: (version: string, style?: string) => unknown;
|
||||
APIPathStyle: { FLAT: string; REST: string };
|
||||
};
|
||||
|
||||
const spec = generateDefinitionForVersion(apiHandler.latestApiVersion, APIPathStyle.FLAT);
|
||||
process.stdout.write(JSON.stringify(spec, null, 2));
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Smoke-test the entry**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cd admin && pnpm exec tsx scripts/dump-spec.ts > /tmp/etherpad-spec.json
|
||||
echo "exit: $?"
|
||||
head -c 200 /tmp/etherpad-spec.json
|
||||
```
|
||||
Expected: exit 0; the head output starts with `{` and contains `"openapi"` and `"paths"`.
|
||||
|
||||
If the script fails because importing `openapi.ts` triggers errors from `Settings`, debug by running `pnpm exec tsx -e "require('../src/node/hooks/express/openapi.ts')"` from `admin/` to isolate. The most likely fix is to set `EP_LOG_DESTINATION=stderr` or similar; do not refactor `Settings` from this PR — note the issue and ask before expanding scope.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add admin/scripts/dump-spec.ts
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore(admin): add OpenAPI spec dump entry (#7638)
|
||||
|
||||
Loaded via tsx by gen-api.mjs in the next commit.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Write the codegen orchestrator
|
||||
|
||||
**Files:**
|
||||
- Create: `admin/scripts/gen-api.mjs`
|
||||
|
||||
- [ ] **Step 1: Create the file**
|
||||
|
||||
```js
|
||||
// admin/scripts/gen-api.mjs
|
||||
//
|
||||
// Regenerates admin/src/api/schema.d.ts from the live OpenAPI spec exported
|
||||
// by src/node/hooks/express/openapi.ts. Run via `pnpm --filter admin gen:api`.
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdtempSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const adminRoot = path.resolve(here, '..');
|
||||
const outFile = path.join(adminRoot, 'src', 'api', 'schema.d.ts');
|
||||
|
||||
const tmpDir = mkdtempSync(path.join(tmpdir(), 'etherpad-openapi-'));
|
||||
const specPath = path.join(tmpDir, 'spec.json');
|
||||
|
||||
try {
|
||||
const dump = spawnSync('pnpm', ['exec', 'tsx', 'scripts/dump-spec.ts'], {
|
||||
cwd: adminRoot,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'inherit'],
|
||||
});
|
||||
if (dump.status !== 0) {
|
||||
console.error(`dump-spec.ts failed with exit code ${dump.status}`);
|
||||
process.exit(dump.status ?? 1);
|
||||
}
|
||||
writeFileSync(specPath, dump.stdout, 'utf8');
|
||||
|
||||
const gen = spawnSync(
|
||||
'pnpm',
|
||||
['exec', 'openapi-typescript', specPath, '-o', outFile],
|
||||
{ cwd: adminRoot, stdio: 'inherit' },
|
||||
);
|
||||
if (gen.status !== 0) {
|
||||
console.error(`openapi-typescript failed with exit code ${gen.status}`);
|
||||
process.exit(gen.status ?? 1);
|
||||
}
|
||||
|
||||
const header =
|
||||
`// GENERATED — do not edit. Run \`pnpm --filter admin gen:api\` to regenerate.\n` +
|
||||
`// Source: src/node/hooks/express/openapi.ts (#7638)\n\n`;
|
||||
const body = readFileSync(outFile, 'utf8');
|
||||
writeFileSync(outFile, header + body, 'utf8');
|
||||
|
||||
console.log(`Wrote ${path.relative(process.cwd(), outFile)}`);
|
||||
} finally {
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add the `gen:api` script and amend `build`**
|
||||
|
||||
In `admin/package.json`, edit the `scripts` block. Before:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"build-copy": "tsc && vite build --outDir ../src/templates/admin --emptyOutDir",
|
||||
"preview": "vite preview"
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"gen:api": "node scripts/gen-api.mjs",
|
||||
"build": "pnpm gen:api && tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"build-copy": "pnpm gen:api && tsc && vite build --outDir ../src/templates/admin --emptyOutDir",
|
||||
"preview": "vite preview"
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run codegen and confirm output**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
mkdir -p admin/src/api
|
||||
pnpm --filter admin gen:api
|
||||
ls -la admin/src/api/schema.d.ts
|
||||
head -10 admin/src/api/schema.d.ts
|
||||
```
|
||||
Expected:
|
||||
- exit 0
|
||||
- `schema.d.ts` exists, > 1 KB
|
||||
- first two lines are the generated header
|
||||
- subsequent lines contain `export interface paths` and entries like `"/api/{version}/createGroup"`
|
||||
|
||||
- [ ] **Step 4: Commit script + package.json + generated schema**
|
||||
|
||||
```bash
|
||||
git add admin/scripts/gen-api.mjs admin/package.json admin/src/api/schema.d.ts
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore(admin): wire OpenAPI codegen into build (#7638)
|
||||
|
||||
Adds `gen:api` script and amends `build`/`build-copy` to regenerate
|
||||
admin/src/api/schema.d.ts before compiling. The generated file is
|
||||
checked in so it shows up in PR review and so a fresh checkout doesn't
|
||||
need codegen to typecheck.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Runtime client module
|
||||
|
||||
**Files:**
|
||||
- Create: `admin/src/api/client.ts`
|
||||
|
||||
- [ ] **Step 1: Create the file**
|
||||
|
||||
```ts
|
||||
// admin/src/api/client.ts
|
||||
//
|
||||
// Typed HTTP client and TanStack Query hooks derived from the generated
|
||||
// OpenAPI schema. Regenerate the schema with `pnpm --filter admin gen:api`.
|
||||
|
||||
import createClient from 'openapi-fetch';
|
||||
import createQueryHooks from 'openapi-react-query';
|
||||
import type { paths } from './schema';
|
||||
|
||||
export const fetchClient = createClient<paths>({ baseUrl: '/' });
|
||||
export const $api = createQueryHooks(fetchClient);
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Confirm typecheck passes**
|
||||
|
||||
Run: `pnpm --filter admin exec tsc --noEmit`
|
||||
Expected: no errors. If `paths` is missing from `schema.d.ts`, rerun `pnpm --filter admin gen:api` (it should have produced an `export interface paths` already in Task 4).
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add admin/src/api/client.ts
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat(admin): typed openapi-fetch + react-query client (#7638)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Query provider with dev-only devtools
|
||||
|
||||
**Files:**
|
||||
- Create: `admin/src/api/QueryProvider.tsx`
|
||||
|
||||
- [ ] **Step 1: Create the file**
|
||||
|
||||
```tsx
|
||||
// admin/src/api/QueryProvider.tsx
|
||||
//
|
||||
// TanStack Query provider for the admin UI. Devtools are loaded lazily and
|
||||
// only in dev builds so they don't ship to production.
|
||||
|
||||
import { lazy, Suspense, useState, type ReactNode } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
const Devtools = import.meta.env.DEV
|
||||
? lazy(() =>
|
||||
import('@tanstack/react-query-devtools').then((m) => ({
|
||||
default: m.ReactQueryDevtools,
|
||||
})),
|
||||
)
|
||||
: null;
|
||||
|
||||
export const QueryProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [client] = useState(
|
||||
() =>
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 30_000,
|
||||
refetchOnWindowFocus: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={client}>
|
||||
{children}
|
||||
{Devtools && (
|
||||
<Suspense fallback={null}>
|
||||
<Devtools initialIsOpen={false} />
|
||||
</Suspense>
|
||||
)}
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Typecheck**
|
||||
|
||||
Run: `pnpm --filter admin exec tsc --noEmit`
|
||||
Expected: no errors.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add admin/src/api/QueryProvider.tsx
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat(admin): TanStack Query provider, dev-only devtools (#7638)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Mount the provider at the admin root
|
||||
|
||||
**Files:**
|
||||
- Modify: `admin/src/main.tsx`
|
||||
|
||||
- [ ] **Step 1: Read the file to confirm current shape**
|
||||
|
||||
Run: `cat admin/src/main.tsx`
|
||||
Expected: matches the structure where `<I18nextProvider>` wraps `<Toast.Provider>` wraps `<RouterProvider>` inside `<React.StrictMode>`.
|
||||
|
||||
- [ ] **Step 2: Edit `admin/src/main.tsx`**
|
||||
|
||||
Add the import after the existing imports:
|
||||
|
||||
```tsx
|
||||
import { QueryProvider } from './api/QueryProvider.tsx';
|
||||
```
|
||||
|
||||
Wrap the existing `<I18nextProvider>...</I18nextProvider>` subtree in `<QueryProvider>`. The render block becomes:
|
||||
|
||||
```tsx
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<QueryProvider>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Toast.Provider>
|
||||
<ToastDialog/>
|
||||
<RouterProvider router={router}/>
|
||||
</Toast.Provider>
|
||||
</I18nextProvider>
|
||||
</QueryProvider>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
```
|
||||
|
||||
(Provider order matters only for context lookups; placing `QueryProvider` outside `I18nextProvider` is fine because it does not consume i18n.)
|
||||
|
||||
- [ ] **Step 3: Typecheck**
|
||||
|
||||
Run: `pnpm --filter admin exec tsc --noEmit`
|
||||
Expected: no errors.
|
||||
|
||||
- [ ] **Step 4: Build the admin bundle**
|
||||
|
||||
Run: `pnpm --filter admin run build`
|
||||
Expected: build succeeds. Output indicates one bundle (no extra chunk for devtools in production — confirm by grepping the `dist/` for `query-devtools` strings; should be absent).
|
||||
|
||||
```bash
|
||||
grep -rn "ReactQueryDevtools" admin/dist/ 2>/dev/null | head
|
||||
```
|
||||
Expected: no matches (production bundle excludes devtools).
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add admin/src/main.tsx
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat(admin): mount TanStack Query provider at root (#7638)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: Smoke test for the client module
|
||||
|
||||
**Files:**
|
||||
- Create: `admin/src/api/__tests__/client.test.ts`
|
||||
|
||||
The admin package does not yet ship a unit test runner. Reuse whatever the rest of admin uses for tests if anything; otherwise, this test runs under `tsx --test` (Node's built-in test runner, no extra deps). Confirm at Step 1.
|
||||
|
||||
- [ ] **Step 1: Detect the test runner**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
grep -E '"(test|vitest|jest)"' admin/package.json
|
||||
ls admin/vitest.config.* admin/jest.config.* 2>/dev/null
|
||||
```
|
||||
|
||||
If admin has no runner configured, use Node's built-in `node:test` (which `tsx` supports).
|
||||
|
||||
- [ ] **Step 2: Create the test file**
|
||||
|
||||
```ts
|
||||
// admin/src/api/__tests__/client.test.ts
|
||||
//
|
||||
// Smoke test that the OpenAPI client module loads and exposes the expected
|
||||
// surface. Catches toolchain wiring regressions (missing peer deps,
|
||||
// generator output that doesn't export `paths`, etc.).
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
test('client module exports fetchClient and $api', async () => {
|
||||
const mod = await import('../client.ts');
|
||||
assert.ok(mod.fetchClient, 'fetchClient export is present');
|
||||
assert.ok(mod.$api, '$api export is present');
|
||||
assert.equal(typeof mod.fetchClient.GET, 'function', 'fetchClient.GET is a function');
|
||||
assert.equal(typeof mod.$api.useQuery, 'function', '$api.useQuery is a function');
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add a `test` script to `admin/package.json`** (only if one does not already exist)
|
||||
|
||||
If `admin/package.json` has no `"test"` script, add:
|
||||
|
||||
```json
|
||||
"test": "tsx --test src/api/__tests__/client.test.ts"
|
||||
```
|
||||
|
||||
If admin already has a test runner (e.g. `vitest`), skip the script addition and instead place the test at the location the existing runner picks up (`*.test.ts` is conventional for both vitest and node:test).
|
||||
|
||||
- [ ] **Step 4: Run the test**
|
||||
|
||||
Run: `pnpm --filter admin test`
|
||||
Expected: 1 test passing.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add admin/src/api/__tests__/client.test.ts admin/package.json
|
||||
git commit -m "$(cat <<'EOF'
|
||||
test(admin): smoke test for typed openapi-fetch client (#7638)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 9: CI freshness check
|
||||
|
||||
**Files:**
|
||||
- Modify: `.github/workflows/frontend-admin-tests.yml`
|
||||
|
||||
Add a step before the existing `Build admin frontend` step that runs codegen and fails if the working tree changed.
|
||||
|
||||
- [ ] **Step 1: Read the current workflow**
|
||||
|
||||
Run: `grep -n "Build admin frontend" .github/workflows/frontend-admin-tests.yml`
|
||||
Expected: a single match around the build step that runs `pnpm run build` from `working-directory: admin`.
|
||||
|
||||
- [ ] **Step 2: Insert the freshness check**
|
||||
|
||||
Insert immediately before the `Build admin frontend` step:
|
||||
|
||||
```yaml
|
||||
- name: Verify admin OpenAPI schema is up to date
|
||||
working-directory: admin
|
||||
run: |
|
||||
pnpm gen:api
|
||||
if ! git diff --exit-code src/api/schema.d.ts; then
|
||||
echo ""
|
||||
echo "::error::admin/src/api/schema.d.ts is out of date."
|
||||
echo "Run \`pnpm --filter admin gen:api\` and commit the result."
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Lint the YAML**
|
||||
|
||||
Run: `python3 -c "import yaml,sys; yaml.safe_load(open('.github/workflows/frontend-admin-tests.yml'))" && echo OK`
|
||||
Expected: `OK`.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add .github/workflows/frontend-admin-tests.yml
|
||||
git commit -m "$(cat <<'EOF'
|
||||
ci(admin): verify generated OpenAPI schema is up to date (#7638)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 10: Documentation
|
||||
|
||||
**Files:**
|
||||
- Create: `admin/README.md`
|
||||
|
||||
- [ ] **Step 1: Create the file**
|
||||
|
||||
```markdown
|
||||
# Admin UI
|
||||
|
||||
Vite + React 19 single-page app served at `/admin`. Talks to the backend over
|
||||
socket.io for the existing settings / plugins / pads pages, and (when
|
||||
endpoints are added to the OpenAPI spec) over a typed REST client.
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | What it does |
|
||||
| -------------------- | -------------------------------------------------------- |
|
||||
| `pnpm dev` | Vite dev server. Expects an etherpad backend on :9001. |
|
||||
| `pnpm gen:api` | Regenerates `src/api/schema.d.ts` from the OpenAPI spec. |
|
||||
| `pnpm build` | `gen:api` + `tsc` + `vite build`. |
|
||||
| `pnpm build-copy` | Same, but writes into `../src/templates/admin`. |
|
||||
| `pnpm test` | Smoke tests for the API client wiring. |
|
||||
| `pnpm lint` | ESLint. |
|
||||
|
||||
## Typed API client
|
||||
|
||||
The admin uses [`openapi-typescript`] to generate types from
|
||||
`src/node/hooks/express/openapi.ts`, [`openapi-fetch`] for typed requests, and
|
||||
[`openapi-react-query`] for TanStack Query bindings.
|
||||
|
||||
[`openapi-typescript`]: https://github.com/openapi-ts/openapi-typescript
|
||||
[`openapi-fetch`]: https://github.com/openapi-ts/openapi-typescript/tree/main/packages/openapi-fetch
|
||||
[`openapi-react-query`]: https://github.com/openapi-ts/openapi-typescript/tree/main/packages/openapi-react-query
|
||||
|
||||
### Regenerating the schema
|
||||
|
||||
```sh
|
||||
pnpm --filter admin gen:api
|
||||
```
|
||||
|
||||
This runs `admin/scripts/gen-api.mjs`, which loads
|
||||
`src/node/hooks/express/openapi.ts`, calls `generateDefinitionForVersion` for
|
||||
the latest API version, pipes the JSON through `openapi-typescript`, and
|
||||
writes the result to `admin/src/api/schema.d.ts`. The generated file is
|
||||
checked in.
|
||||
|
||||
Run `gen:api` after any change to:
|
||||
|
||||
- `src/node/hooks/express/openapi.ts`
|
||||
- `src/node/handler/APIHandler.ts` (changes to `latestApiVersion`)
|
||||
- the resource definitions referenced by `openapi.ts`
|
||||
|
||||
### CI freshness check
|
||||
|
||||
`.github/workflows/frontend-admin-tests.yml` runs `pnpm gen:api` and fails the
|
||||
build if `admin/src/api/schema.d.ts` is out of date. If you see the failure
|
||||
locally, run `pnpm --filter admin gen:api` and commit the regenerated file.
|
||||
|
||||
### Using the client
|
||||
|
||||
```tsx
|
||||
import { $api } from './api/client';
|
||||
|
||||
const SettingsPanel = () => {
|
||||
const { data } = $api.useQuery('get', '/admin/settings'); // example
|
||||
return <pre>{JSON.stringify(data, null, 2)}</pre>;
|
||||
};
|
||||
```
|
||||
|
||||
The admin endpoints are not yet present in the OpenAPI spec — this client is
|
||||
in place to support upcoming work (see issue #7638 follow-up). For now, it is
|
||||
exercised only by the smoke test.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add admin/README.md
|
||||
git commit -m "$(cat <<'EOF'
|
||||
docs(admin): document OpenAPI codegen workflow (#7638)
|
||||
|
||||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 11: Full verification pass
|
||||
|
||||
No new files — this task confirms the work is green end-to-end before pushing.
|
||||
|
||||
- [ ] **Step 1: Clean rebuild**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
pnpm --filter admin gen:api
|
||||
pnpm --filter admin run build
|
||||
```
|
||||
Expected: both succeed.
|
||||
|
||||
- [ ] **Step 2: Repo-wide typecheck**
|
||||
|
||||
Run: `pnpm ts-check`
|
||||
Expected: no new errors versus baseline. If there are pre-existing errors, confirm none are in files this PR touched.
|
||||
|
||||
- [ ] **Step 3: Admin tests**
|
||||
|
||||
Run: `pnpm --filter admin test`
|
||||
Expected: 1 test passing.
|
||||
|
||||
- [ ] **Step 4: Backend unit tests** (sanity — `openapi.ts` change)
|
||||
|
||||
Run: `pnpm test` (or the narrowest available suite covering the API hook; if the full suite is slow, run specs that exercise `openapi.ts` only).
|
||||
Expected: green.
|
||||
|
||||
- [ ] **Step 5: Confirm devtools absent from production bundle**
|
||||
|
||||
Run: `grep -rn "ReactQueryDevtools" admin/dist/ 2>/dev/null`
|
||||
Expected: zero matches.
|
||||
|
||||
- [ ] **Step 6: Manual smoke**
|
||||
|
||||
Per project convention (memory: install plugin/branch for manual test), install this branch on a local etherpad and:
|
||||
- Open `/admin/` in a dev build (`pnpm --filter admin dev`). Confirm the React Query devtools panel button appears in the bottom corner.
|
||||
- Open `/admin/` in the production-built bundle. Confirm devtools panel is absent.
|
||||
- Click through plugins / settings / pads / shout pages and confirm no regression versus pre-PR behavior (existing socket.io flows unchanged).
|
||||
|
||||
Document the smoke results in the PR description.
|
||||
|
||||
- [ ] **Step 7: Push**
|
||||
|
||||
```bash
|
||||
git push -u fork chore/admin-typesafe-api-7638
|
||||
```
|
||||
|
||||
- [ ] **Step 8: Open PR**
|
||||
|
||||
```bash
|
||||
gh pr create \
|
||||
--repo johnmclear/etherpad-lite \
|
||||
--title "chore(admin): typesafe API client + TanStack Query rails (#7638)" \
|
||||
--body "$(cat <<'EOF'
|
||||
## Summary
|
||||
|
||||
Lays down the rails for a typesafe, OpenAPI-derived admin API client backed by TanStack Query. Closes #7638.
|
||||
|
||||
- Codegen toolchain (`pnpm --filter admin gen:api`) producing `admin/src/api/schema.d.ts` from `src/node/hooks/express/openapi.ts`.
|
||||
- Runtime client (`openapi-fetch` + `openapi-react-query`).
|
||||
- `<QueryProvider>` mounted at the admin root with dev-only devtools.
|
||||
- CI freshness check on the generated schema.
|
||||
- `admin/README.md` documenting the workflow.
|
||||
|
||||
**No call sites migrated.** Admin endpoints aren't in the OpenAPI spec yet — that gap is filed as a follow-up issue and must land before any migration is useful. #7601 should rebase onto this branch.
|
||||
|
||||
**Semver:** patch — build tooling + currently-unused runtime libs, no observable behavior change.
|
||||
|
||||
## Test plan
|
||||
|
||||
- [x] `pnpm --filter admin gen:api` runs clean
|
||||
- [x] `pnpm --filter admin run build` succeeds
|
||||
- [x] `pnpm --filter admin test` passes (smoke test)
|
||||
- [x] `pnpm ts-check` clean
|
||||
- [x] Production bundle does not contain devtools
|
||||
- [x] Manual smoke: dev build shows devtools, prod build hides them, existing socket.io pages unaffected
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
- [ ] **Step 9: Trigger Qodo review** (per project convention)
|
||||
|
||||
```bash
|
||||
gh pr comment <PR-number> --repo johnmclear/etherpad-lite --body "/review"
|
||||
```
|
||||
|
||||
- [ ] **Step 10: File the spec-coverage follow-up issue**
|
||||
|
||||
Create a new issue on `ether/etherpad` titled "Document admin endpoints in the OpenAPI spec" and link from the PR body. The issue should note that 7638 rails are unused until admin endpoints are added.
|
||||
|
||||
---
|
||||
|
||||
## Risk register (carried from spec)
|
||||
|
||||
- **`openapi.ts` not cleanly importable.** If `dump-spec.ts` fails to import the module due to side effects (Settings, log4js init), pause and ask before refactoring `Settings`. A common workaround is to set `EP_LOG_DESTINATION=stderr` or set `NODE_ENV=production`. Do not silently expand scope.
|
||||
- **Generated schema differs by Node version.** `openapi-typescript` output is deterministic, but if a contributor sees a phantom diff, confirm Node major matches the CI matrix (22/24/25 today; CI uses 24 on PRs).
|
||||
- **Bundle size.** ~12 KB gzipped added to the admin bundle even with no call sites. Acceptable; flagged in the PR body for transparency.
|
||||
|
||||
## Out of scope (do not pull in)
|
||||
|
||||
- Adding admin endpoints to the OpenAPI spec.
|
||||
- Migrating any `fetch()` site in `admin/src/`.
|
||||
- Backend handler changes.
|
||||
- Pad-side frontend changes.
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
# Issue 7638 — Typesafe Admin API Client + TanStack Query Rails
|
||||
|
||||
**Status:** design approved 2026-05-01
|
||||
**Issue:** https://github.com/ether/etherpad/issues/7638
|
||||
**Related:** #7601 (introduces new admin REST sites that will adopt these rails)
|
||||
|
||||
## Goal
|
||||
|
||||
Lay down the toolchain and runtime rails for a typesafe, OpenAPI-derived admin
|
||||
API client backed by TanStack Query. Do not migrate any existing call sites.
|
||||
|
||||
## Why rails-only
|
||||
|
||||
The issue's framing ("migrate every `useEffect`+`fetch` site") overstates what is
|
||||
actually present in `admin/src/` today.
|
||||
|
||||
- The only REST `fetch()` sites are `App.tsx` and `LoginScreen.tsx` (both POST to
|
||||
`/admin-auth/`) and `i18n.ts` (locale loading).
|
||||
- All admin pages with real data flow (Settings, Plugins, Pads, Shout) run over
|
||||
socket.io + zustand, not REST.
|
||||
- The OpenAPI spec produced by `src/node/hooks/express/openapi.ts` only covers
|
||||
the public Etherpad HTTP API under `/api/{version}/*`. It documents zero admin
|
||||
endpoints — no `/admin-auth/`, no future `/admin/*` REST endpoints from #7601.
|
||||
|
||||
So the generated client has nothing in `admin/src/` to type today. The value of
|
||||
landing this PR now is to get the rails in place so #7601 (and any subsequent
|
||||
admin REST work) can adopt them on day one.
|
||||
|
||||
A separate issue will be filed to add admin endpoint coverage to the OpenAPI
|
||||
spec; until that lands, no migrations are useful.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Admin endpoint coverage in the OpenAPI spec (separate issue).
|
||||
- Migrating any existing `fetch()` call site.
|
||||
- Backend changes.
|
||||
- Pad-side frontend.
|
||||
|
||||
## Toolchain
|
||||
|
||||
| Package | Type | Purpose |
|
||||
| -------------------------------- | -------------- | ---------------------------------------- |
|
||||
| `openapi-typescript` | devDependency | Generates `.d.ts` from the OpenAPI spec |
|
||||
| `openapi-fetch` | dependency | Typed `fetch` wrapper |
|
||||
| `openapi-react-query` | dependency | TanStack Query bindings over the client |
|
||||
| `@tanstack/react-query` | dependency | Query runtime |
|
||||
| `@tanstack/react-query-devtools` | dependency | Dev-only devtools panel |
|
||||
|
||||
All added to `admin/package.json`. No version pinning beyond standard caret
|
||||
ranges; pick the latest stable at implementation time.
|
||||
|
||||
## Codegen (option 3, hybrid)
|
||||
|
||||
One checked-in artifact, CI-enforced freshness.
|
||||
|
||||
### Script: `admin/scripts/gen-api.mjs`
|
||||
|
||||
1. Imports the spec-building entry point from
|
||||
`src/node/hooks/express/openapi.ts` (or a thin wrapper module that calls
|
||||
the spec builder without booting Express). Writes the resulting spec JSON
|
||||
to a temp file in `os.tmpdir()`.
|
||||
2. Shells out:
|
||||
`openapi-typescript <tmp> -o admin/src/api/schema.d.ts`.
|
||||
3. Prepends a generated header comment to the output:
|
||||
`// GENERATED — do not edit. Run \`pnpm gen:api\` to regenerate.`
|
||||
4. Removes the temp file.
|
||||
|
||||
If `openapi.ts` cannot be loaded as an ES module without side effects (e.g.
|
||||
because it imports settings or boots an Express app at import time), the
|
||||
implementation must extract the pure spec-builder into a dedicated module so
|
||||
the script can call it cleanly. That refactor is in scope; the touch should be
|
||||
minimal.
|
||||
|
||||
### Wiring
|
||||
|
||||
- `admin/package.json`:
|
||||
- `"scripts": { "gen:api": "node scripts/gen-api.mjs", ... }`.
|
||||
- `"build"` is amended to run `gen:api` before `tsc && vite build` so a
|
||||
fresh checkout builds without manual steps.
|
||||
- Root `package.json`: existing admin build entry point invokes the same
|
||||
script (or relies on `admin/package.json`'s amended `build`).
|
||||
|
||||
### Generated output
|
||||
|
||||
- Path: `admin/src/api/schema.d.ts`.
|
||||
- Checked in.
|
||||
- First line: generated-header comment.
|
||||
|
||||
### CI freshness check
|
||||
|
||||
A CI job (folded into the existing admin lint workflow if practical, otherwise
|
||||
a new step) runs:
|
||||
|
||||
```bash
|
||||
pnpm --filter admin gen:api
|
||||
git diff --exit-code admin/src/api/schema.d.ts
|
||||
```
|
||||
|
||||
If the diff is non-empty, CI fails with a message instructing the contributor
|
||||
to run `pnpm --filter admin gen:api` and commit the result.
|
||||
|
||||
## Runtime client
|
||||
|
||||
### `admin/src/api/client.ts`
|
||||
|
||||
```ts
|
||||
import createClient from "openapi-fetch";
|
||||
import createQueryHooks from "openapi-react-query";
|
||||
import type { paths } from "./schema";
|
||||
|
||||
export const fetchClient = createClient<paths>({ baseUrl: "/" });
|
||||
export const $api = createQueryHooks(fetchClient);
|
||||
```
|
||||
|
||||
### `admin/src/api/QueryProvider.tsx`
|
||||
|
||||
- Wraps children in `QueryClientProvider`.
|
||||
- Single shared `QueryClient` constructed once (module-level or `useState`
|
||||
initializer), with defaults:
|
||||
- `staleTime: 30_000`
|
||||
- `refetchOnWindowFocus: true`
|
||||
- Other defaults left at library defaults.
|
||||
- Mounts `ReactQueryDevtools` only when `import.meta.env.DEV` is true. Use a
|
||||
dynamic `import()` so devtools do not ship in the production bundle.
|
||||
|
||||
### `admin/src/main.tsx`
|
||||
|
||||
Wrap `<App />` in `<QueryProvider>`. No other changes.
|
||||
|
||||
## Documentation
|
||||
|
||||
`admin/README.md` (create or extend) documents:
|
||||
|
||||
- How to regenerate: `pnpm --filter admin gen:api`.
|
||||
- When to regenerate: after any change to `src/node/hooks/express/openapi.ts`
|
||||
or anything that affects the spec it builds.
|
||||
- What gets regenerated: `admin/src/api/schema.d.ts` only.
|
||||
- The CI freshness check and how to recover from a failing check.
|
||||
- A short "how to use the client" snippet showing
|
||||
`$api.useQuery("get", "/some/path")` once admin endpoints are in the spec.
|
||||
|
||||
## Tests
|
||||
|
||||
- **Module-load smoke test** (`admin/src/api/__tests__/client.test.ts` or
|
||||
similar, matching whatever test infra `admin/` already uses): imports
|
||||
`$api` and `fetchClient`, asserts both are defined. This catches toolchain
|
||||
wiring breakage (missing peer deps, bad export shape, etc.).
|
||||
- **CI freshness check** (above) is the test for spec/schema sync.
|
||||
- **Manual smoke after PR install:** install the branch on the local
|
||||
Etherpad, open `/admin`, confirm:
|
||||
- Existing socket.io flows (settings, plugins, pads) still work — no
|
||||
regressions from the `<QueryProvider>` wrap.
|
||||
- React Query devtools panel appears in a dev build (`pnpm --filter admin
|
||||
dev`) and is absent from a production build.
|
||||
|
||||
Note: per project convention, the user expects automated tests before manual
|
||||
verification, but the manual smoke is unavoidable here because devtools
|
||||
visibility and provider wrap are runtime concerns. The smoke check is a
|
||||
secondary safety net, not the primary test strategy.
|
||||
|
||||
## Branch / PR plan
|
||||
|
||||
- Fork: `johnmclear/etherpad-lite` (per project convention; never commit
|
||||
directly to `ether/etherpad-lite`).
|
||||
- Branch: `chore/admin-typesafe-api-7638`.
|
||||
- Base: latest `main` of the fork, after syncing from `ether/etherpad-lite`.
|
||||
- PR title: `chore(admin): typesafe API client + TanStack Query rails`.
|
||||
- PR body declares semver: **patch** (build tooling + unused runtime libs;
|
||||
no observable behavior change).
|
||||
- PR body links #7638 and notes:
|
||||
- Rails-only — no call site migrations.
|
||||
- Separate spec-coverage issue to follow.
|
||||
- #7601 should rebase onto this branch once merged.
|
||||
|
||||
## Risks
|
||||
|
||||
- **`openapi.ts` not cleanly importable.** If pulling the spec builder out
|
||||
requires touching production paths, that risk needs a small refactor PR
|
||||
first. Mitigation: keep the extraction surgical; if it grows, split into
|
||||
its own PR and rebase 7638 on top.
|
||||
- **Bundle size.** TanStack Query + react-query bindings add ~12 KB gzipped
|
||||
to the admin bundle even with no call sites using it. Acceptable for an
|
||||
internal admin UI; flag in PR body for transparency.
|
||||
- **Provider wrap regressions.** `<QueryProvider>` wrapping `<App />` should
|
||||
be inert for socket.io paths but the manual smoke confirms.
|
||||
|
||||
## Definition of done
|
||||
|
||||
- `pnpm --filter admin gen:api` runs cleanly on a fresh checkout.
|
||||
- `pnpm --filter admin build` succeeds.
|
||||
- `admin/src/api/schema.d.ts` is checked in with the generated header.
|
||||
- `<QueryProvider>` wraps `<App />`; devtools visible in dev, absent in
|
||||
production build.
|
||||
- CI freshness check is wired and passing.
|
||||
- `admin/README.md` documents the codegen workflow.
|
||||
- Manual smoke confirms no regression in existing socket.io-driven pages.
|
||||
- PR opened against `johnmclear/etherpad-lite`, semver labelled patch,
|
||||
Qodo `/review` triggered after push.
|
||||
240
pnpm-lock.yaml
generated
240
pnpm-lock.yaml
generated
|
|
@ -46,6 +46,18 @@ importers:
|
|||
'@radix-ui/react-switch':
|
||||
specifier: ^1.2.6
|
||||
version: 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.100.9
|
||||
version: 5.100.9(react@19.2.5)
|
||||
'@tanstack/react-query-devtools':
|
||||
specifier: ^5.100.9
|
||||
version: 5.100.9(@tanstack/react-query@5.100.9(react@19.2.5))(react@19.2.5)
|
||||
openapi-fetch:
|
||||
specifier: ^0.17.0
|
||||
version: 0.17.0
|
||||
openapi-react-query:
|
||||
specifier: ^0.5.4
|
||||
version: 0.5.4(@tanstack/react-query@5.100.9(react@19.2.5))(openapi-fetch@0.17.0)
|
||||
devDependencies:
|
||||
'@radix-ui/react-dialog':
|
||||
specifier: ^1.1.15
|
||||
|
|
@ -89,6 +101,9 @@ importers:
|
|||
lucide-react:
|
||||
specifier: ^1.14.0
|
||||
version: 1.14.0(react@19.2.5)
|
||||
openapi-typescript:
|
||||
specifier: ^7.13.0
|
||||
version: 7.13.0(typescript@6.0.3)
|
||||
react:
|
||||
specifier: ^19.2.5
|
||||
version: 19.2.5
|
||||
|
|
@ -107,6 +122,9 @@ importers:
|
|||
socket.io-client:
|
||||
specifier: ^4.8.3
|
||||
version: 4.8.3
|
||||
tsx:
|
||||
specifier: ^4.21.0
|
||||
version: 4.21.0
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
|
|
@ -159,7 +177,7 @@ importers:
|
|||
version: 0.129.0
|
||||
vitepress:
|
||||
specifier: ^2.0.0-alpha.17
|
||||
version: 2.0.0-alpha.17(@types/node@25.6.0)(jwt-decode@4.0.0)(lightningcss@1.32.0)(oxc-minify@0.129.0)(postcss@8.5.14)(tsx@4.21.0)(typescript@6.0.3)
|
||||
version: 2.0.0-alpha.17(@types/node@25.6.0)(change-case@5.4.4)(jwt-decode@4.0.0)(lightningcss@1.32.0)(oxc-minify@0.129.0)(postcss@8.5.14)(tsx@4.21.0)(typescript@6.0.3)
|
||||
|
||||
src:
|
||||
dependencies:
|
||||
|
|
@ -1615,6 +1633,16 @@ packages:
|
|||
peerDependencies:
|
||||
'@redis/client': ^5.12.1
|
||||
|
||||
'@redocly/ajv@8.11.2':
|
||||
resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
|
||||
|
||||
'@redocly/config@0.22.0':
|
||||
resolution: {integrity: sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==}
|
||||
|
||||
'@redocly/openapi-core@1.34.14':
|
||||
resolution: {integrity: sha512-y+xFx+Zz54Xhr8jUdnLENYnt7Y7GEDL6Q03ga7rTtX8DVwefX9H+hQEPgJp1nda7vdH+wJ9/HBVvyfBuW9x6rA==}
|
||||
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
|
||||
|
||||
'@rolldown/binding-android-arm64@1.0.0-rc.17':
|
||||
resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
|
|
@ -1918,6 +1946,23 @@ packages:
|
|||
'@swc/helpers@0.5.21':
|
||||
resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==}
|
||||
|
||||
'@tanstack/query-core@5.100.9':
|
||||
resolution: {integrity: sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ==}
|
||||
|
||||
'@tanstack/query-devtools@5.100.9':
|
||||
resolution: {integrity: sha512-gqiptrTIhbK2PuCaPRHmWXfJG1NGYVFpAr0HqogEqiSBNB5xDz6fmesQt7w4WgMOqOQPnPHJ3ZDMuhDaXvNO8g==}
|
||||
|
||||
'@tanstack/react-query-devtools@5.100.9':
|
||||
resolution: {integrity: sha512-mM3slaVGXJmz+pOLgXdANj75ikgQCyudyl3kmFvm6brI1JyVeY/+IeD17uDHIvZrD8hfoO2sdZ54RFsHdYAuhA==}
|
||||
peerDependencies:
|
||||
'@tanstack/react-query': ^5.100.9
|
||||
react: ^18 || ^19
|
||||
|
||||
'@tanstack/react-query@5.100.9':
|
||||
resolution: {integrity: sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
|
||||
'@tediousjs/connection-string@1.1.0':
|
||||
resolution: {integrity: sha512-z9ZBWEG+8pIB5V1zYzlRPXx0oRJ5H7coPnMQK8EZOw03UTPI9Umn6viL36f5w+CuqkKsnCM50RVStpjZmR0Bng==}
|
||||
|
||||
|
|
@ -2552,6 +2597,10 @@ packages:
|
|||
ajv@8.18.0:
|
||||
resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
|
||||
|
||||
ansi-colors@4.1.3:
|
||||
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -2772,6 +2821,9 @@ packages:
|
|||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
change-case@5.4.4:
|
||||
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
|
||||
|
||||
character-entities-html4@2.1.0:
|
||||
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
|
||||
|
||||
|
|
@ -2808,6 +2860,9 @@ packages:
|
|||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
colorette@1.4.0:
|
||||
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
|
@ -3575,9 +3630,6 @@ packages:
|
|||
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
get-tsconfig@4.13.0:
|
||||
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
|
||||
|
||||
get-tsconfig@4.14.0:
|
||||
resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==}
|
||||
|
||||
|
|
@ -3762,6 +3814,10 @@ packages:
|
|||
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||
engines: {node: '>=0.8.19'}
|
||||
|
||||
index-to-position@1.2.0:
|
||||
resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
|
|
@ -3946,6 +4002,10 @@ packages:
|
|||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
js-levenshtein@1.1.6:
|
||||
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
js-md4@0.3.2:
|
||||
resolution: {integrity: sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==}
|
||||
|
||||
|
|
@ -4292,6 +4352,10 @@ packages:
|
|||
minimatch@3.1.5:
|
||||
resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
|
||||
|
||||
minimatch@5.1.9:
|
||||
resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
minimatch@9.0.9:
|
||||
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
|
@ -4502,6 +4566,15 @@ packages:
|
|||
resolution: {integrity: sha512-1tfLpC+7CajKv08vuFOLm4t8rJvJyqKuyau5IIIrGg3YuQYhmP7JqDL6p6WnbDCusmh3krCrKXoHB6hLF/iHcQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
openapi-fetch@0.17.0:
|
||||
resolution: {integrity: sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==}
|
||||
|
||||
openapi-react-query@0.5.4:
|
||||
resolution: {integrity: sha512-V9lRiozjHot19/BYSgXYoyznDxDJQhEBSdi26+SJ0UqjMANLQhkni4XG+Z7e3Ag7X46ZLMrL9VxYkghU3QvbWg==}
|
||||
peerDependencies:
|
||||
'@tanstack/react-query': ^5.80.0
|
||||
openapi-fetch: ^0.17.0
|
||||
|
||||
openapi-schema-validation@0.4.2:
|
||||
resolution: {integrity: sha512-K8LqLpkUf2S04p2Nphq9L+3bGFh/kJypxIG2NVGKX0ffzT4NQI9HirhiY6Iurfej9lCu7y4Ndm4tv+lm86Ck7w==}
|
||||
|
||||
|
|
@ -4511,6 +4584,15 @@ packages:
|
|||
openapi-types@12.1.3:
|
||||
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
||||
|
||||
openapi-typescript-helpers@0.1.0:
|
||||
resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==}
|
||||
|
||||
openapi-typescript@7.13.0:
|
||||
resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: ^5.x
|
||||
|
||||
optional-js@2.3.0:
|
||||
resolution: {integrity: sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==}
|
||||
|
||||
|
|
@ -4545,6 +4627,10 @@ packages:
|
|||
package-json-from-dist@1.0.1:
|
||||
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
||||
|
||||
parse-json@8.3.0:
|
||||
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
parse5@7.3.0:
|
||||
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
||||
|
||||
|
|
@ -4638,6 +4724,10 @@ packages:
|
|||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
pluralize@8.0.0:
|
||||
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
possible-typed-array-names@1.1.0:
|
||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -5229,6 +5319,10 @@ packages:
|
|||
resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==}
|
||||
engines: {node: '>=14.18.0'}
|
||||
|
||||
supports-color@10.2.2:
|
||||
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -5387,6 +5481,10 @@ packages:
|
|||
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
type-fest@4.41.0:
|
||||
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
type-is@2.0.1:
|
||||
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
|
@ -5490,6 +5588,9 @@ packages:
|
|||
peerDependencies:
|
||||
browserslist: '>= 4.21.0'
|
||||
|
||||
uri-js-replace@1.0.1:
|
||||
resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==}
|
||||
|
||||
uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
|
||||
|
|
@ -5821,6 +5922,9 @@ packages:
|
|||
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
yaml-ast-parser@0.0.43:
|
||||
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
|
||||
|
||||
yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -6866,6 +6970,29 @@ snapshots:
|
|||
dependencies:
|
||||
'@redis/client': 5.12.1(@opentelemetry/api@1.9.1)
|
||||
|
||||
'@redocly/ajv@8.11.2':
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
uri-js-replace: 1.0.1
|
||||
|
||||
'@redocly/config@0.22.0': {}
|
||||
|
||||
'@redocly/openapi-core@1.34.14(supports-color@10.2.2)':
|
||||
dependencies:
|
||||
'@redocly/ajv': 8.11.2
|
||||
'@redocly/config': 0.22.0
|
||||
colorette: 1.4.0
|
||||
https-proxy-agent: 7.0.6(supports-color@10.2.2)
|
||||
js-levenshtein: 1.1.6
|
||||
js-yaml: 4.1.1
|
||||
minimatch: 5.1.9
|
||||
pluralize: 8.0.0
|
||||
yaml-ast-parser: 0.0.43
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@rolldown/binding-android-arm64@1.0.0-rc.17':
|
||||
optional: true
|
||||
|
||||
|
|
@ -7069,6 +7196,21 @@ snapshots:
|
|||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@tanstack/query-core@5.100.9': {}
|
||||
|
||||
'@tanstack/query-devtools@5.100.9': {}
|
||||
|
||||
'@tanstack/react-query-devtools@5.100.9(@tanstack/react-query@5.100.9(react@19.2.5))(react@19.2.5)':
|
||||
dependencies:
|
||||
'@tanstack/query-devtools': 5.100.9
|
||||
'@tanstack/react-query': 5.100.9(react@19.2.5)
|
||||
react: 19.2.5
|
||||
|
||||
'@tanstack/react-query@5.100.9(react@19.2.5)':
|
||||
dependencies:
|
||||
'@tanstack/query-core': 5.100.9
|
||||
react: 19.2.5
|
||||
|
||||
'@tediousjs/connection-string@1.1.0': {}
|
||||
|
||||
'@tootallnate/quickjs-emscripten@0.23.0': {}
|
||||
|
|
@ -7458,7 +7600,7 @@ snapshots:
|
|||
debug: 4.4.3(supports-color@8.1.1)
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.9
|
||||
minimatch: 10.2.5
|
||||
semver: 7.7.4
|
||||
ts-api-utils: 1.4.3(typescript@6.0.3)
|
||||
optionalDependencies:
|
||||
|
|
@ -7698,12 +7840,13 @@ snapshots:
|
|||
'@vueuse/shared': 14.2.1(vue@3.5.30(typescript@6.0.3))
|
||||
vue: 3.5.30(typescript@6.0.3)
|
||||
|
||||
'@vueuse/integrations@14.2.1(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.30(typescript@6.0.3))':
|
||||
'@vueuse/integrations@14.2.1(change-case@5.4.4)(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.30(typescript@6.0.3))':
|
||||
dependencies:
|
||||
'@vueuse/core': 14.2.1(vue@3.5.30(typescript@6.0.3))
|
||||
'@vueuse/shared': 14.2.1(vue@3.5.30(typescript@6.0.3))
|
||||
vue: 3.5.30(typescript@6.0.3)
|
||||
optionalDependencies:
|
||||
change-case: 5.4.4
|
||||
focus-trap: 8.0.0
|
||||
jwt-decode: 4.0.0
|
||||
|
||||
|
|
@ -7757,6 +7900,8 @@ snapshots:
|
|||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
ansi-colors@4.1.3: {}
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
|
||||
ansi-regex@6.2.2: {}
|
||||
|
|
@ -7996,6 +8141,8 @@ snapshots:
|
|||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
change-case@5.4.4: {}
|
||||
|
||||
character-entities-html4@2.1.0: {}
|
||||
|
||||
character-entities-legacy@3.0.0: {}
|
||||
|
|
@ -8026,6 +8173,8 @@ snapshots:
|
|||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
colorette@1.4.0: {}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
|
@ -8148,6 +8297,12 @@ snapshots:
|
|||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
debug@4.4.3(supports-color@10.2.2):
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
optionalDependencies:
|
||||
supports-color: 10.2.2
|
||||
|
||||
debug@4.4.3(supports-color@8.1.1):
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
|
@ -8469,7 +8624,7 @@ snapshots:
|
|||
'@rushstack/eslint-patch': 1.16.1
|
||||
'@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/parser': 7.18.0(eslint@10.3.0)(typescript@6.0.3)
|
||||
eslint-import-resolver-typescript: 3.9.1(eslint-plugin-import@2.32.0)(eslint@10.3.0)
|
||||
eslint-import-resolver-typescript: 3.9.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0))(eslint@10.3.0)
|
||||
eslint-plugin-cypress: 2.15.2(eslint@10.3.0)
|
||||
eslint-plugin-eslint-comments: 3.2.0(eslint@10.3.0)
|
||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint-import-resolver-typescript@3.9.1)(eslint@10.3.0)
|
||||
|
|
@ -8493,7 +8648,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-import-resolver-typescript@3.9.1(eslint-plugin-import@2.32.0)(eslint@10.3.0):
|
||||
eslint-import-resolver-typescript@3.9.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0))(eslint@10.3.0):
|
||||
dependencies:
|
||||
'@nolyfill/is-core-module': 1.0.39
|
||||
debug: 4.4.3(supports-color@8.1.1)
|
||||
|
|
@ -8508,14 +8663,14 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.1(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.9.1)(eslint@10.3.0):
|
||||
eslint-module-utils@2.12.1(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.9.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0))(eslint@10.3.0))(eslint@10.3.0):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 7.18.0(eslint@10.3.0)(typescript@6.0.3)
|
||||
eslint: 10.3.0
|
||||
eslint-import-resolver-node: 0.3.10
|
||||
eslint-import-resolver-typescript: 3.9.1(eslint-plugin-import@2.32.0)(eslint@10.3.0)
|
||||
eslint-import-resolver-typescript: 3.9.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0))(eslint@10.3.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -8548,7 +8703,7 @@ snapshots:
|
|||
doctrine: 2.1.0
|
||||
eslint: 10.3.0
|
||||
eslint-import-resolver-node: 0.3.10
|
||||
eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.9.1)(eslint@10.3.0)
|
||||
eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.9.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0))(eslint@10.3.0))(eslint@10.3.0)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.16.1
|
||||
is-glob: 4.0.3
|
||||
|
|
@ -8944,10 +9099,6 @@ snapshots:
|
|||
es-errors: 1.3.0
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
get-tsconfig@4.13.0:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
|
||||
get-tsconfig@4.14.0:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
|
|
@ -9157,6 +9308,13 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
https-proxy-agent@7.0.6(supports-color@10.2.2):
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
debug: 4.4.3(supports-color@10.2.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
i18next-browser-languagedetector@8.2.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
|
|
@ -9177,6 +9335,8 @@ snapshots:
|
|||
|
||||
imurmurhash@0.1.4: {}
|
||||
|
||||
index-to-position@1.2.0: {}
|
||||
|
||||
inherits@2.0.4: {}
|
||||
|
||||
internal-slot@1.1.0:
|
||||
|
|
@ -9348,6 +9508,8 @@ snapshots:
|
|||
|
||||
js-cookie@3.0.5: {}
|
||||
|
||||
js-levenshtein@1.1.6: {}
|
||||
|
||||
js-md4@0.3.2: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
|
@ -9701,6 +9863,10 @@ snapshots:
|
|||
dependencies:
|
||||
brace-expansion: 1.1.14
|
||||
|
||||
minimatch@5.1.9:
|
||||
dependencies:
|
||||
brace-expansion: 5.0.5
|
||||
|
||||
minimatch@9.0.9:
|
||||
dependencies:
|
||||
brace-expansion: 2.1.0
|
||||
|
|
@ -9946,6 +10112,16 @@ snapshots:
|
|||
openapi-types: 12.1.3
|
||||
qs: 6.15.0
|
||||
|
||||
openapi-fetch@0.17.0:
|
||||
dependencies:
|
||||
openapi-typescript-helpers: 0.1.0
|
||||
|
||||
openapi-react-query@0.5.4(@tanstack/react-query@5.100.9(react@19.2.5))(openapi-fetch@0.17.0):
|
||||
dependencies:
|
||||
'@tanstack/react-query': 5.100.9(react@19.2.5)
|
||||
openapi-fetch: 0.17.0
|
||||
openapi-typescript-helpers: 0.1.0
|
||||
|
||||
openapi-schema-validation@0.4.2:
|
||||
dependencies:
|
||||
jsonschema: 1.2.4
|
||||
|
|
@ -9961,6 +10137,18 @@ snapshots:
|
|||
|
||||
openapi-types@12.1.3: {}
|
||||
|
||||
openapi-typescript-helpers@0.1.0: {}
|
||||
|
||||
openapi-typescript@7.13.0(typescript@6.0.3):
|
||||
dependencies:
|
||||
'@redocly/openapi-core': 1.34.14(supports-color@10.2.2)
|
||||
ansi-colors: 4.1.3
|
||||
change-case: 5.4.4
|
||||
parse-json: 8.3.0
|
||||
supports-color: 10.2.2
|
||||
typescript: 6.0.3
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
optional-js@2.3.0: {}
|
||||
|
||||
optionator@0.9.4:
|
||||
|
|
@ -10029,6 +10217,12 @@ snapshots:
|
|||
|
||||
package-json-from-dist@1.0.1: {}
|
||||
|
||||
parse-json@8.3.0:
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.29.0
|
||||
index-to-position: 1.2.0
|
||||
type-fest: 4.41.0
|
||||
|
||||
parse5@7.3.0:
|
||||
dependencies:
|
||||
entities: 6.0.1
|
||||
|
|
@ -10107,6 +10301,8 @@ snapshots:
|
|||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
pluralize@8.0.0: {}
|
||||
|
||||
possible-typed-array-names@1.1.0: {}
|
||||
|
||||
postcss@8.5.14:
|
||||
|
|
@ -10830,6 +11026,8 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
supports-color@10.2.2: {}
|
||||
|
||||
supports-color@7.2.0:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
|
@ -10967,7 +11165,7 @@ snapshots:
|
|||
tsx@4.21.0:
|
||||
dependencies:
|
||||
esbuild: 0.27.1
|
||||
get-tsconfig: 4.13.0
|
||||
get-tsconfig: 4.14.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
|
|
@ -10981,6 +11179,8 @@ snapshots:
|
|||
|
||||
type-fest@0.20.2: {}
|
||||
|
||||
type-fest@4.41.0: {}
|
||||
|
||||
type-is@2.0.1:
|
||||
dependencies:
|
||||
content-type: 1.0.5
|
||||
|
|
@ -11132,6 +11332,8 @@ snapshots:
|
|||
escalade: 3.2.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
uri-js-replace@1.0.1: {}
|
||||
|
||||
uri-js@4.4.1:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
|
@ -11208,7 +11410,7 @@ snapshots:
|
|||
fsevents: 2.3.3
|
||||
tsx: 4.21.0
|
||||
|
||||
vitepress@2.0.0-alpha.17(@types/node@25.6.0)(jwt-decode@4.0.0)(lightningcss@1.32.0)(oxc-minify@0.129.0)(postcss@8.5.14)(tsx@4.21.0)(typescript@6.0.3):
|
||||
vitepress@2.0.0-alpha.17(@types/node@25.6.0)(change-case@5.4.4)(jwt-decode@4.0.0)(lightningcss@1.32.0)(oxc-minify@0.129.0)(postcss@8.5.14)(tsx@4.21.0)(typescript@6.0.3):
|
||||
dependencies:
|
||||
'@docsearch/css': 4.6.0
|
||||
'@docsearch/js': 4.6.0
|
||||
|
|
@ -11222,7 +11424,7 @@ snapshots:
|
|||
'@vue/devtools-api': 8.1.0
|
||||
'@vue/shared': 3.5.30
|
||||
'@vueuse/core': 14.2.1(vue@3.5.30(typescript@6.0.3))
|
||||
'@vueuse/integrations': 14.2.1(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.30(typescript@6.0.3))
|
||||
'@vueuse/integrations': 14.2.1(change-case@5.4.4)(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.30(typescript@6.0.3))
|
||||
focus-trap: 8.0.0
|
||||
mark.js: 8.11.1
|
||||
minisearch: 7.2.0
|
||||
|
|
@ -11417,6 +11619,8 @@ snapshots:
|
|||
|
||||
yallist@5.0.0: {}
|
||||
|
||||
yaml-ast-parser@0.0.43: {}
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
yargs-unparser@2.0.0:
|
||||
|
|
|
|||
|
|
@ -769,3 +769,6 @@ const generateServerForApiVersion = (apiRoot:string, req:any): {
|
|||
} => ({
|
||||
url: `${settings.ssl ? 'https' : 'http'}://${req.headers.host}${apiRoot}`,
|
||||
});
|
||||
|
||||
exports.generateDefinitionForVersion = generateDefinitionForVersion;
|
||||
exports.APIPathStyle = APIPathStyle;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue