Skip to content
Merged
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
95 changes: 1 addition & 94 deletions .github/workflows/postgrest-typegen-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ on:
paths:
- "packages/postgrest-typegen/**"
- ".github/workflows/postgrest-typegen-validate.yml"
schedule:
- cron: "0 6 * * *" # nightly drift check against real postgres-meta
workflow_dispatch: {}

defaults:
run:
Expand All @@ -21,10 +18,9 @@ defaults:
jobs:
test:
name: Types, lint, build, test
if: github.event_name == 'push' || github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Install bun
run: curl -fsSL https://bun.sh/install | bash
- name: Add bun to PATH
Expand All @@ -35,92 +31,3 @@ jobs:
- run: bun run knip
- run: bun run build
- run: bun run test

parity-check:
name: Parity vs. real postgres-meta
# Nightly/manual only, never on push or pull_request: postgres-meta can
# drift for reasons unrelated to any change here, and that shouldn't
# block or flake unrelated PRs (same pattern as capability-matrix's
# "Tier 2" reference checks).
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
steps:
- name: Checkout supabase/sdk
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Checkout supabase/postgres-meta
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: supabase/postgres-meta
path: postgres-meta
- name: Install bun
run: curl -fsSL https://bun.sh/install | bash
- name: Add bun to PATH
run: echo "$HOME/.bun/bin" >> "$GITHUB_PATH"
- run: bun install --frozen-lockfile
- name: Load shared fixture schema
run: bun scripts/load-fixtures.ts
- name: Generate postgrest-typegen output
run: |
mkdir -p /tmp/ours
for lang in typescript go python swift; do
bun scripts/print-generated.ts "$lang" > "/tmp/ours/$lang.txt"
done
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
- name: Install postgres-meta dependencies
working-directory: postgres-meta
run: npm ci
- name: Generate postgres-meta output
working-directory: postgres-meta
env:
PG_META_DB_URL: postgresql://postgres:postgres@localhost:5432/postgres
run: |
mkdir -p /tmp/theirs
for lang in typescript go python swift; do
PG_META_GENERATE_TYPES="$lang" node --loader ts-node/esm src/server/server.ts > "/tmp/theirs/$lang.txt"
done
- name: Canonicalize postgres-meta's TypeScript output
# postgres-meta formats with prettier; this package defaults to
# oxfmt, which has different line-wrap heuristics (e.g. printWidth is
# matched at 80, but wrapping decisions for nested object types can
# still differ). Reformatting postgres-meta's output through our own
# formatter first means the diff below reflects content, not which
# formatter produced it.
run: bun scripts/canonicalize-typescript.ts /tmp/theirs/typescript.txt > /tmp/theirs/typescript.canonical.txt
- name: Compare outputs
# Order-independent: sortGeneratorMetadata canonicalizes collection
# order (semantic schema+name, not postgres-meta's environment- and
# data-dependent heap order) across all four generators, which is a
# deliberate improvement over postgres-meta's real behavior, not a
# divergence to flag. Comparing declaration BLOCKS (paragraphs
# separated by a blank line) as a sorted set, rather than raw bytes,
# verifies the two agree on content without being sensitive to that
# reordering.
run: |
mv /tmp/theirs/typescript.canonical.txt /tmp/theirs/typescript.txt
normalize() { awk 'BEGIN{RS="";FS="\n"} {gsub(/\n/, "\\n"); print}' "$1" | sort; }
failed=0
for lang in typescript go python swift; do
if ! diff -u <(normalize "/tmp/theirs/$lang.txt") <(normalize "/tmp/ours/$lang.txt"); then
echo "::error::$lang output diverges from real postgres-meta"
failed=1
fi
done
exit "$failed"
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This is a lightweight monorepo of shared tooling for the Supabase client SDKs. P
- `packages/capability-matrix/` — the **canonical feature registry** for all Supabase client SDKs — not the SDKs themselves. It defines what features exist (name, description, grouping) across 7 client SDKs (JavaScript, Flutter, Python, Swift, C#, Go, Kotlin). SDKs declare compliance separately in their own repos via `sdk-compliance.yaml` files. The output is a static capability matrix website at https://supabase.github.io/sdk/ showing which features each SDK implements.
- `packages/dart-symbol-extractor/` — Dart public API symbol extractor used by the Dart compliance workflow.
- `packages/go-symbol-extractor/` — Go public API symbol extractor used by the Go compliance workflow.
- `packages/postgrest-typegen/` — introspects a PostgreSQL schema into the `GeneratorMetadata` contract and generates PostgREST types for TypeScript, Go, Python, and Swift. See its own `CLAUDE.md` for architecture and the byte-parity constraint with postgres-meta.
- `packages/postgrest-typegen/` — introspects a PostgreSQL schema into the `GeneratorMetadata` contract and generates PostgREST types for TypeScript, Go, Python, and Swift. See its own `CLAUDE.md` for architecture and how postgres-meta and the CLI consume it.

## Commands

Expand Down
32 changes: 17 additions & 15 deletions packages/postgrest-typegen/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,23 @@ bun run format-and-lint # oxfmt + oxlint check
bun run knip # unused-code/deps check
```

## Byte-Parity Constraint

This package must produce **byte-identical** output to postgres-meta's
templates until parity is validated and released. Two consequences:

- `prettier` is pinned **exact** to the version postgres-meta's own lockfile
currently resolves (not a caret range, and not just "latest"), since a
version mismatch reformats generator output and silently breaks parity with
the real upstream CLI, not just with this package's own snapshots. Check
postgres-meta's resolved version before bumping. Bumping means regenerating
every inline snapshot (`bun test --update-snapshots`) and the parity
fixtures under `test/parity/expected/`, and reviewing the diff.
- Don't "improve" template strings or SQL for existing generators without
regenerating snapshots/fixtures first. Byte parity first; behavior-changing
cleanups (e.g. oxfmt instead of prettier) come later.
## Relationship to postgres-meta

postgres-meta deleted its own templates and consumes this package for all four
languages (supabase/postgres-meta#1084, shipped in postgres-meta v0.99.0 on
2026-08-31), and so does the CLI (supabase/cli#6404). This package is the
single source of truth for generator output: there is no upstream to stay
byte-identical with, and there is no cross-check against postgres-meta (its
output is whatever version of this package its lockfile pins, so such a check
only ever measured release lag). Consequences:

- Every generator output change is a behavior change for every consumer.
Regenerate the inline snapshots (`bun test --update-snapshots`) and the
goldens under `test/parity/expected/` in the same PR, review that diff
deliberately, and describe the output change in the PR so it reaches the
release notes consumers read before bumping.
- Don't reformat or "clean up" template strings casually; a formatter-only
change still churns every consumer's generated files.

SQL literal quoting: the introspection queries use `literal()` from
`src/introspection/sql/pg-format.ts`, an inlined port of `pg-format@1.0.4`
Expand Down
16 changes: 8 additions & 8 deletions packages/postgrest-typegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ JSON output, not inside `postgrest-typegen`.

The TypeScript, Go, Python, and Swift generators bundled here are a
**deliberate transition**, not the target architecture for new languages.
They were ported byte-parity from postgres-meta's own templates so
`supabase gen types` keeps working unchanged while postgres-meta's copies get
deprecated in favor of this package. It's decided that all four eventually
move out to their own SDK repos too (tracked in SDK-1641), not yet scheduled
— sequenced after postgres-meta's cutover to this package settles.
They were ported byte-for-byte from postgres-meta's own templates so
`supabase gen types` kept working unchanged; postgres-meta has since deleted
its copies and consumes this package instead. It's decided that all four
eventually move out to their own SDK repos too (tracked in SDK-1641), not yet
scheduled — sequenced after postgres-meta's cutover to this package settles.

> [!IMPORTANT]
> **We are no longer accepting contributions that fix the bundled generators.**
> Because each of the four is moving out to its own language's SDK repository,
> a fix landed here would have to be made a second time in the destination
> repository, and it also breaks byte parity with postgres-meta until a release
> propagates. Please open an issue describing the bug instead, so it can be
> tracked and carried across the move.
> repository, and postgres-meta and the CLI only pick it up once they bump
> their pinned version of this package. Please open an issue describing the
> bug instead, so it can be tracked and carried across the move.

## Design

Expand Down
8 changes: 1 addition & 7 deletions packages/postgrest-typegen/knip.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": [
"test/global-setup.ts",
"test/**/*.test.ts",
"scripts/load-fixtures.ts",
"scripts/print-generated.ts",
"scripts/canonicalize-typescript.ts"
],
"entry": ["test/global-setup.ts", "test/**/*.test.ts"],
"ignoreBinaries": ["oxfmt", "oxlint"]
}
33 changes: 0 additions & 33 deletions packages/postgrest-typegen/scripts/canonicalize-typescript.ts

This file was deleted.

30 changes: 0 additions & 30 deletions packages/postgrest-typegen/scripts/load-fixtures.ts

This file was deleted.

53 changes: 0 additions & 53 deletions packages/postgrest-typegen/scripts/print-generated.ts

This file was deleted.

32 changes: 12 additions & 20 deletions packages/postgrest-typegen/test/parity/parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,22 @@ import { introspect } from "../../src/introspection/index.ts";
import type { GeneratorMetadata } from "../../src/types.ts";

/**
* End-to-end parity gate: introspect the shared postgres-meta fixture DB, run
* all four generators with their default options, and assert the output matches
* the committed golden files in `expected/`.
* End-to-end golden gate: introspect the shared fixture DB, run all four
* generators with their default options, and assert the output matches the
* committed golden files in `expected/`.
*
* The metadata is passed through `sortGeneratorMetadata` first (as every
* consumer must), so the golden files reflect the canonical, deterministic
* ordering rather than the database's heap order. The goldens were originally
* captured from a real postgres-meta CLI run against this fixture DB; only the
* ordering of order-sensitive collections (Go/Python/Swift emit tables/views
* in metadata order) is canonicalized — and postgres-meta applies the same
* sort pass, so the two stay in lockstep. postgres-meta's CLI prints with
* `console.log`, which appends exactly one trailing newline to the
* generator's return value — hence the `+ "\n"` below.
* ordering rather than the database's heap order. Consumers print the
* generator's return value with `console.log`, which appends exactly one
* trailing newline, hence the `+ "\n"` below.
*
* CAUTION — this only proves parity at the moment a golden was captured. The
* established workflow for an *intentional* generator change is to
* regenerate the goldens from this package itself and review the diff, which
* means a generator change and its golden update land in the same PR: the
* gate then only proves internal self-consistency (generator output matches
* its own golden), not agreement with postgres-meta's actual current output.
* It cannot catch a regression introduced by that same change. Re-verify
* against a real postgres-meta run periodically, and always whenever
* postgres-meta's own templates change (e.g. the upstream fixes ported into
* this package) rather than trusting a self-regenerated golden alone.
* The goldens were originally captured from postgres-meta's own templates
* before it cut over to this package; since then they are regenerated from
* this package whenever a generator changes on purpose. A generator change
* and its golden update land in the same PR, so this gate proves output does
* not change *unintentionally*, and the reviewed golden diff is where an
* intentional change gets scrutinized.
*/
const FIXTURE_DIR = join(import.meta.dir, "..", "introspection", "fixtures");
const EXPECTED_DIR = join(import.meta.dir, "expected");
Expand Down