mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-21 17:18:37 +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>
78 lines
2.8 KiB
JavaScript
78 lines
2.8 KiB
JavaScript
// 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 });
|
|
}
|