Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
import { execFileSync } from 'node:child_process';
import { writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import openapiTS, { astToString, UNKNOWN } from 'openapi-typescript';

const SPEC_URL = 'https://api.supabase.com/api/v1-json';
const OUTPUT_PATH = new URL('../src/management-api/types.ts', import.meta.url);
const V1_SPEC_URL = 'https://api.supabase.com/api/v1-json';
const V1_OUTPUT_PATH = new URL(
'../src/management-api/types.ts',
import.meta.url
);
// The v2 document is vendored rather than fetched: `/api/v2-json` is not
// served yet, so a URL pull would make regeneration depend on a deployment.
//
// Provenance. The vendored file is generation input and is never hand-edited.
// It is the Management API repo's `api/apps/mgmt-api/specs/v2.spec.json` at
// rates head af464cca85, whose bytes hash to sha256
// dfe848df7543e03d92f25c29a41829f980c99fdca8a326f5903aab1a83c70b26. The copy
// here differs from those bytes in whitespace only, because this repo's
// formatter checks every JSON file it can parse and a generated artifact that
// fails the formatter would fail CI. The documents are equal as documents:
// their canonical JSON (keys sorted, no insignificant whitespace) hashes to
// sha256 1b5a1548e91e81d1e6951019cd84ad2a8e193ca1fe2ef7f1578dd6a40c8ebb92 on
// both sides.
//
// Refresh, once `/api/v2-json` is served: copy the served document over this
// file, run `pnpm format`, then re-check content equality against the served
// bytes the same way, canonically rather than byte for byte.
const V2_SPEC_PATH = new URL(
'./specs/management-api-v2.spec.json',
import.meta.url
);
const V2_OUTPUT_PATH = new URL(
'../src/management-api/v2-types.ts',
import.meta.url
);

// Recursive "any JSON value" schemas produce a self-referential type that TypeScript
// rejects (TS2502). These fields are opaque blobs anyway, so `unknown` is fine.
Expand All @@ -14,13 +44,34 @@ function transform(schemaObject, options) {
return undefined;
}

const ast = await openapiTS(SPEC_URL, { transform });
/**
* Generates one document's types. Each API version owns its own module: the v1
* contract every current caller depends on stays exactly as it was, and v2
* arrives beside it.
*/
async function generate(spec, outputPath) {
const ast = await openapiTS(spec, { transform });

const output = `/**
const output = `/**
* This file was auto-generated by openapi-typescript.
* Do not make direct changes to the file.
*/

${astToString(ast)}`;

writeFileSync(OUTPUT_PATH, output);
writeFileSync(outputPath, output);
}

await generate(V1_SPEC_URL, V1_OUTPUT_PATH);
await generate(V2_SPEC_PATH, V2_OUTPUT_PATH);

// The v1 output predates this repo's formatter and is excluded from it, so it
// stays exactly as the generator emits it. The v2 output is new and is not
// excluded, so it is formatted here instead: a freshly generated tree then
// passes `pnpm format:check`, and regeneration stays reproducible because the
// formatter is idempotent.
execFileSync(
'pnpm',
['exec', 'biome', 'format', '--write', fileURLToPath(V2_OUTPUT_PATH)],
{ stdio: 'inherit' }
);
Loading
Loading