Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
039866a
feat(cli): generate types natively with postgrest-typegen (CLI-2279)
claude Aug 31, 2026
6d8c60d
fix(cli): adapt gen types TLS to the server and bundle prettier cleanly
claude Aug 31, 2026
d522373
Merge origin/develop into claude/postgrest-typegen-cli-wud64e
claude Aug 31, 2026
ebb449b
feat(cli): upgrade postgrest-typegen to 0.2.0 (oxfmt formatter)
claude Aug 31, 2026
282b509
Merge origin/develop into claude/postgrest-typegen-cli-wud64e
avallete Aug 31, 2026
446f55d
fix(cli): restore gen types TLS pinning and timeout parity
avallete Aug 31, 2026
2d2cd81
fix(cli): pin gen types CA only when the SSL probe reports TLS
avallete Aug 31, 2026
39cb157
chore(cli): trim leftover gen types review nits
avallete Aug 31, 2026
9e2e29d
fix(cli): mark oxfmt optional plugins external on musl builds
avallete Aug 31, 2026
b3fd2dc
fix(cli): retry gen types pooler fallback on native ENOTFOUND
avallete Sep 1, 2026
2edc793
fix(cli): typecheck published typegen types instead of bun source
avallete Sep 1, 2026
ff72277
merge: origin/develop into claude/postgrest-typegen-cli-wud64e
avallete Sep 1, 2026
4894323
fix(cli): keep typegen type pins off bun's tsconfig
avallete Sep 1, 2026
4eb2b6e
fix(cli): generate types with postgrest-typegen 0.2.1
avallete Sep 2, 2026
abdb6aa
merge: origin/develop into claude/postgrest-typegen-cli-wud64e
avallete Sep 2, 2026
8ce5cfa
fix(cli): reject sub-second gen types timeouts that disable the bound
avallete Sep 2, 2026
5b2115a
fix(cli): warn when gen types ignores --network-id
avallete Sep 2, 2026
baea74f
Merge branch 'develop' into claude/postgrest-typegen-cli-wud64e
avallete Sep 3, 2026
174ebfd
fix(cli): fail closed on gen types TLS probe and warn pre-path --netw…
avallete Sep 3, 2026
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
13 changes: 11 additions & 2 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"test:e2e:run": "bun --bun vitest run --project e2e",
"test:live": "bun --bun vitest run --project live",
"test:smoke": "bun run tests/smoke-test.ts",
"types:check": "tsc --noEmit"
"types:check": "tsc --noEmit -p tsconfig.types.json"
},
"dependencies": {
"eciesjs": "^0.5.0",
Expand All @@ -57,6 +57,14 @@
"@effect/vitest": "catalog:",
"@modelcontextprotocol/sdk": "^1.30.0",
"@napi-rs/keyring": "^1.3.0",
"@oxfmt/binding-darwin-arm64": "0.65.0",
"@oxfmt/binding-darwin-x64": "0.65.0",
"@oxfmt/binding-linux-arm64-gnu": "0.65.0",
"@oxfmt/binding-linux-arm64-musl": "0.65.0",
"@oxfmt/binding-linux-x64-gnu": "0.65.0",
"@oxfmt/binding-linux-x64-musl": "0.65.0",
"@oxfmt/binding-win32-arm64-msvc": "0.65.0",
"@oxfmt/binding-win32-x64-msvc": "0.65.0",
"@parcel/watcher": "^2.6.0",
"@parcel/watcher-darwin-arm64": "2.6.0",
"@parcel/watcher-darwin-x64": "2.6.0",
Expand All @@ -68,8 +76,9 @@
"@parcel/watcher-win32-x64": "2.6.0",
"@supabase/api": "workspace:*",
"@supabase/config": "workspace:*",
"@supabase/pg-delta": "1.0.0-alpha.47",
"@supabase/pg-delta": "1.0.0-alpha.48",
"@supabase/pg-topo": "1.0.0-alpha.6",
"@supabase/postgrest-typegen": "0.2.1",
"@supabase/process-compose": "workspace:*",
"@supabase/stack": "workspace:*",
"@tsconfig/bun": "catalog:",
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/scripts/build-binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { $ } from "bun";
import process from "node:process";

import { bundleServeMainTemplate } from "../src/shared/functions/serve-main-bundler.ts";
import { oxfmtExternalArgs } from "./bundle-externals.ts";

/**
* Compile a single CLI shell to a standalone binary, embedding the pre-bundled
Expand Down Expand Up @@ -30,4 +31,4 @@ const defineArg = `--define=SUPABASE_FUNCTIONS_SERVE_MAIN_TEMPLATE=${JSON.string
await bundleServeMainTemplate(),
)}`;

await $`bun build ${entrypoint} --compile ${versionDefine} ${defineArg} --outfile ${outfile}`;
await $`bun build ${entrypoint} --compile ${versionDefine} ${defineArg} ${oxfmtExternalArgs} --outfile ${outfile}`;
3 changes: 3 additions & 0 deletions apps/cli/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from "node:path";
import process from "node:process";
import { parseArgs } from "node:util";
import { bundleServeMainTemplate } from "../src/shared/functions/serve-main-bundler.ts";
import { oxfmtExternalArgs } from "./bundle-externals.ts";
import { darwinBinariesForShell, MACOS_IDENTIFIERS } from "./macos-signing.ts";

const MUSL_TARGETS = [
Expand Down Expand Up @@ -152,6 +153,7 @@ async function buildTarget(target: (typeof TARGETS)[number]) {
`--define=SUPABASE_LIBC=${JSON.stringify(libc)}`,
serveMainTemplateDefine,
...posthogBuildDefines,
...oxfmtExternalArgs,
`--outfile=${outfile}`,
]);
console.log(`[${target.pkg}] Done.`);
Expand Down Expand Up @@ -301,6 +303,7 @@ async function buildMuslBinaries() {
`--define=SUPABASE_LIBC=${JSON.stringify(libc)}`,
serveMainTemplateDefine,
...posthogBuildDefines,
...oxfmtExternalArgs,
`--outfile=${outfile}`,
]);

Expand Down
22 changes: 22 additions & 0 deletions apps/cli/scripts/bundle-externals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Optional prettier plugins that `oxfmt`'s dist lazily `import()`s for
* non-TypeScript file types (liquid, pug, astro, …). They are not installed —
* gen types only ever formats generated TypeScript, through the statically
* embedded binding in `src/legacy/commands/gen/types/types.oxfmt.ts` — but
* `bun build` still tries to resolve every analyzable dynamic import, so each
* one must be marked external for the compile to succeed. Shared by the dev
* build (`build-binary.ts`) and the multi-target release build (`build.ts`).
*/
export const OXFMT_OPTIONAL_PLUGIN_EXTERNALS = [
"@prettier/plugin-hermes",
"@prettier/plugin-oxc",
"@prettier/plugin-pug",
"@shopify/prettier-plugin-liquid",
"@zackad/prettier-plugin-twig",
"prettier-plugin-astro",
"prettier-plugin-marko",
] as const;

export const oxfmtExternalArgs = OXFMT_OPTIONAL_PLUGIN_EXTERNALS.map(
(name) => `--external=${name}`,
);
165 changes: 112 additions & 53 deletions apps/cli/src/legacy/commands/gen/types/SIDE_EFFECTS.md

Large diffs are not rendered by default.

This file was deleted.

This file was deleted.

This file was deleted.

19 changes: 1 addition & 18 deletions apps/cli/src/legacy/commands/gen/types/types.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
ensureImage,
resolveDeadline,
} from "../../../../../tests/helpers/docker-image.ts";
import { resolvePgmetaImage } from "./types.shared.ts";

const TYPEGEN_LANGS = ["typescript", "go", "swift", "python"] as const;
type TypegenLang = (typeof TYPEGEN_LANGS)[number];
Expand Down Expand Up @@ -209,26 +208,12 @@ async function waitForLocalPostgres(containerName: string) {
);
}

// `gen types` starts pg-meta itself (local AND remote non-ts languages) via a
// single-registry rewrite with no fallback (`resolvePgmetaImage`), so pre-resolve
// it and retag the winning candidate onto the exact reference the CLI will run.
async function ensurePgmetaImage(deadline?: number) {
const expected = resolvePgmetaImage();
const resolved = await ensureImage(dockerfileServiceImage("pgmeta"), deadline);
if (resolved !== expected) {
await expectDockerSucceeded(["tag", resolved, expected], 30_000);
}
}

async function startLocalPostgres(input: { readonly projectId: string; readonly dbPort: number }) {
const containerName = localDbContainerId(input.projectId);
const networkName = localNetworkId(input.projectId);
// One shared window (already counted in the local test's timeout), with
// pg-meta's slice reserved up front: Postgres may spend the window only up
// to the point that still leaves pg-meta the default budget.
// One shared window, already counted in the local test's timeout.
const imageDeadline = resolveDeadline(LOCAL_IMAGE_BUDGET_MS);
const postgresImage = await ensureImage(LOCAL_POSTGRES_IMAGE, imageDeadline - RESOLVE_BUDGET_MS);
await ensurePgmetaImage(imageDeadline);

await expectDockerSucceeded(["network", "create", networkName], 30_000);
await expectDockerSucceeded(
Expand Down Expand Up @@ -404,8 +389,6 @@ describe("legacy gen types e2e", () => {
);
}

await ensurePgmetaImage();

for (const lang of TYPEGEN_LANGS) {
const result = await runSupabase(
["gen", "types", "--project-id", remoteProjectRef, "--lang", lang, "--schema", "public"],
Expand Down
22 changes: 18 additions & 4 deletions apps/cli/src/legacy/commands/gen/types/types.errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,26 @@ export class LegacyInvalidGenTypesDurationError extends Data.TaggedError(
}
}

export class LegacyInvalidGenTypesDatabaseUrlError extends Data.TaggedError(
"LegacyInvalidGenTypesDatabaseUrlError",
)<{
/**
* A `postgrest-typegen` introspection query failed against a live database
* the CLI successfully connected to. Schema-derived, so a database finding.
*/
export class LegacyGenTypesMetadataError extends Data.TaggedError("LegacyGenTypesMetadataError")<{
readonly message: string;
}> {
get [ErrorActionabilityId](): CliErrorActionabilityDeclaration {
return actionability.provideFlags;
return actionability.dbFinding;
Comment thread
avallete marked this conversation as resolved.
}
}

/**
* Language generation or formatting failed after introspection succeeded —
* a CLI packaging / formatter / template defect, not a user schema finding.
*/
export class LegacyGenTypesGenerateError extends Data.TaggedError("LegacyGenTypesGenerateError")<{
Comment thread
avallete marked this conversation as resolved.
readonly message: string;
}> {
get [ErrorActionabilityId](): CliErrorActionabilityDeclaration {
return actionability.internalPanic;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, expect, it } from "@effect/vitest";
import { BunServices } from "@effect/platform-bun";
import { Effect, Exit, Layer } from "effect";

import {
LegacyPgDeltaSslProbe,
LegacyPgDeltaSslProbeError,
} from "../../../shared/legacy-pgdelta-ssl-probe.service.ts";
import { legacyGenTypesGeneratorLayer } from "./types.generator.layer.ts";
import { LegacyGenTypesGenerator } from "./types.generator.ts";

function failingProbeLayer(message: string) {
return Layer.succeed(LegacyPgDeltaSslProbe, {
requireSsl: () => Effect.fail(new LegacyPgDeltaSslProbeError({ message })),
requireSslForHost: () => Effect.fail(new LegacyPgDeltaSslProbeError({ message })),
});
}

describe("legacyGenTypesGeneratorLayer", () => {
it.live("fails closed when the remote SSL probe cannot determine TLS capability", () => {
const layer = legacyGenTypesGeneratorLayer.pipe(
Layer.provide(failingProbeLayer("connection refused")),
Layer.provide(BunServices.layer),
);

return Effect.gen(function* () {
const generator = yield* LegacyGenTypesGenerator;
const exit = yield* generator
.generate({
conn: {
host: "db.example.com",
port: 5432,
user: "postgres",
password: "secret",
database: "postgres",
},
isLocal: false,
dnsResolver: "https",
lang: "go",
includedSchemas: [],
postgrestV9Compat: false,
swiftAccessControl: "internal",
queryTimeoutSeconds: 15,
})
.pipe(Effect.exit);

expect(Exit.isFailure(exit)).toBe(true);
if (Exit.isFailure(exit)) {
expect(String(exit.cause)).toContain("LegacyPgDeltaSslProbeError");
expect(String(exit.cause)).toContain("connection refused");
}
}).pipe(Effect.provide(layer));
});
});
Loading
Loading