diff --git a/generator/package.json b/generator/package.json index ca988ea..3529181 100644 --- a/generator/package.json +++ b/generator/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "generate": "bun ./src/index.ts", + "generate": "bun ./src/index.ts --snapshot=${INTROSPECTION_SNAPSHOT:-../../clients/.introspection/snapshot.json}", "start": "bun ./src/index.ts" }, "devDependencies": { diff --git a/generator/src/fetch.ts b/generator/src/fetch.ts index 3952c15..2d7ec33 100644 --- a/generator/src/fetch.ts +++ b/generator/src/fetch.ts @@ -36,12 +36,34 @@ export type IntrospectPayload = { }[]; }; -export let getEndpointVersions = async (url: string) => - (await fetch(new URL('/metorial/introspect/versions', url)).then(res => res.json())) as { - versions: { version: string; displayVersion: string; isCurrent: boolean }[]; - }; +export type IntrospectVersions = { + versions: { version: string; displayVersion: string; isCurrent: boolean }[]; +}; + +export type IntrospectionSnapshot = { + versions: IntrospectVersions; + endpoints: Record; +}; + +export let getEndpointVersions = async (url: string, snapshot?: IntrospectionSnapshot) => { + if (snapshot) return snapshot.versions; + + return (await fetch(new URL('/metorial/introspect/versions', url)).then(res => + res.json() + )) as IntrospectVersions; +}; + +export let getEndpoints = async ( + url: string, + version: string, + snapshot?: IntrospectionSnapshot +) => { + if (snapshot) { + let payload = snapshot.endpoints[version]; + if (!payload) throw new Error(`Missing introspection snapshot for ${version}.`); + return payload; + } -export let getEndpoints = async (url: string, version: string) => { return (await fetch( new URL(`/metorial/introspect/endpoints?version=${version}`, url) ).then(res => res.json())) as IntrospectPayload; diff --git a/generator/src/index.ts b/generator/src/index.ts index ee6b120..ee12efb 100644 --- a/generator/src/index.ts +++ b/generator/src/index.ts @@ -1,7 +1,7 @@ import path from 'node:path'; import { $ } from 'bun'; import { emitCatalogGo } from './emit/catalog.go'; -import { getEndpoints, getEndpointVersions } from './fetch'; +import { getEndpoints, getEndpointVersions, type IntrospectionSnapshot } from './fetch'; import { joinManifest } from './join'; import { loadManifest } from './manifest'; @@ -16,14 +16,21 @@ let args = process.argv.slice(2); let urlArg = args.find(arg => !arg.startsWith('-')); let urls = urlArg ? urlArg.split(',') : defaultUrls; -let workingUrl = await resolveUrl(urls); +// An explicit URL (argument or API_URL) wins over --snapshot so a running instance can be used. +let snapshotPath = args.find(arg => arg.startsWith('--snapshot='))?.slice('--snapshot='.length); +let snapshot = + snapshotPath && !urlArg && !process.env.API_URL + ? ((await Bun.file(path.resolve(snapshotPath)).json()) as IntrospectionSnapshot) + : undefined; + +let workingUrl = snapshot ? 'snapshot' : await resolveUrl(urls); if (!workingUrl) { throw new Error(`None of the API URLs are reachable: ${urls.join(', ')}`); } let manifestPath = path.join(repoRoot, 'manifest/admin.yaml'); let manifest = await loadManifest(manifestPath); -let versions = await getEndpointVersions(workingUrl); +let versions = await getEndpointVersions(workingUrl, snapshot); let version = versions.versions.find(item => item.version === manifest.apiVersion) || versions.versions.find(item => item.version.includes('magnetar')); @@ -32,7 +39,7 @@ if (!version) { throw new Error(`API at ${workingUrl} does not expose ${manifest.apiVersion}`); } -let payload = await getEndpoints(workingUrl, version.version); +let payload = await getEndpoints(workingUrl, version.version, snapshot); let catalog = joinManifest({ manifest, endpoints: payload.endpoints,