Disambiguates the Phoenix/auth client lib from lib-arcadia-agents-client. Dir lib-arcadia-client → lib-arcadia-core-client; alias updated in tsconfig paths, vite config, app.css @source, imports, CI and docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
100 lines
3.9 KiB
JavaScript
100 lines
3.9 KiB
JavaScript
#!/usr/bin/env node
|
|
// Fetches the arcadia OpenAPI spec and regenerates ../src/generated/openapi.d.ts.
|
|
//
|
|
// Usage (from a consuming app, with arcadia reachable):
|
|
// node ../lib-arcadia-core-client/scripts/sync-spec.mjs
|
|
//
|
|
// Configurable via env:
|
|
// ARCADIA_OPENAPI_URL default: http://localhost:4000/api/openapi
|
|
// ARCADIA_BEARER_TOKEN optional, sent as Authorization if the spec route
|
|
// is gated in your deployment
|
|
//
|
|
// Requires `openapi-typescript` to be available (in the consuming app's
|
|
// node_modules, or globally). Run from a consuming app so the dep resolves:
|
|
// npm i -D openapi-typescript
|
|
|
|
import { writeFile, mkdir } from "node:fs/promises";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
import { dirname, resolve } from "node:path";
|
|
import { createRequire } from "node:module";
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const outDir = resolve(here, "..", "src", "generated");
|
|
const outFile = resolve(outDir, "openapi.d.ts");
|
|
const specUrl = process.env.ARCADIA_OPENAPI_URL ?? "http://localhost:4000/api/openapi";
|
|
|
|
async function main() {
|
|
console.log(`→ fetching ${specUrl}`);
|
|
const headers = {};
|
|
if (process.env.ARCADIA_BEARER_TOKEN) {
|
|
headers["Authorization"] = `Bearer ${process.env.ARCADIA_BEARER_TOKEN}`;
|
|
}
|
|
const res = await fetch(specUrl, { headers });
|
|
if (!res.ok) {
|
|
console.error(`spec fetch failed: ${res.status} ${res.statusText}`);
|
|
process.exit(1);
|
|
}
|
|
const spec = await res.json();
|
|
|
|
// Resolve openapi-typescript from the consuming app's node_modules, since
|
|
// the lib has no package.json of its own.
|
|
let openapiTs;
|
|
let astToString;
|
|
try {
|
|
const requireFromCwd = createRequire(resolve(process.cwd(), "package.json"));
|
|
const resolved = requireFromCwd.resolve("openapi-typescript");
|
|
const mod = await import(pathToFileURL(resolved).href);
|
|
// Handle both ESM (mod.default is the function) and CJS-via-import
|
|
// (mod.default is the namespace, mod.default.default is the function).
|
|
openapiTs = typeof mod.default === "function" ? mod.default : mod.default?.default;
|
|
astToString = mod.astToString ?? mod.default?.astToString;
|
|
} catch (err) {
|
|
console.error(
|
|
`openapi-typescript could not be resolved from ${process.cwd()}.\n` +
|
|
`Run from your consuming app's directory after installing it:\n` +
|
|
` cd <app> && npm i -D openapi-typescript\n` +
|
|
`Original error: ${err.message}`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Scrub malformed operation entries. Arcadia's spec contains some
|
|
// endpoints whose operation value is the bare string "ok" (a controller
|
|
// placeholder that never got replaced with a real OperationObject).
|
|
// openapi-typescript can't transform them — strip them and report so
|
|
// the generation succeeds for the rest. Fix is on the arcadia side.
|
|
const skipped = [];
|
|
if (spec.paths) {
|
|
for (const [path, item] of Object.entries(spec.paths)) {
|
|
if (!item || typeof item !== "object") continue;
|
|
for (const [verb, op] of Object.entries(item)) {
|
|
if (typeof op !== "object" || op === null) {
|
|
skipped.push(`${verb.toUpperCase()} ${path}`);
|
|
delete item[verb];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (skipped.length) {
|
|
console.warn(`⚠ skipped ${skipped.length} malformed operations (arcadia spec bug):`);
|
|
for (const s of skipped.slice(0, 10)) console.warn(` - ${s}`);
|
|
if (skipped.length > 10) console.warn(` …and ${skipped.length - 10} more`);
|
|
}
|
|
|
|
console.log(`→ generating types`);
|
|
const ast = await openapiTs(spec);
|
|
const dts = astToString(ast);
|
|
await mkdir(outDir, { recursive: true });
|
|
const banner =
|
|
"// AUTO-GENERATED — do not edit by hand. Regenerate with sync-spec.mjs.\n" +
|
|
`// Source: ${specUrl}\n` +
|
|
`// Generated: ${new Date().toISOString()}\n\n`;
|
|
await writeFile(outFile, banner + dts, "utf8");
|
|
console.log(`✓ wrote ${outFile}`);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|