Skip to content

feat(cli): generate types natively with postgrest-typegen - #6404

Open
avallete wants to merge 16 commits into
developfrom
claude/postgrest-typegen-cli-wud64e
Open

feat(cli): generate types natively with postgrest-typegen#6404
avallete wants to merge 16 commits into
developfrom
claude/postgrest-typegen-cli-wud64e

Conversation

@avallete

@avallete avallete commented Aug 31, 2026

Copy link
Copy Markdown
Member

Summary

Replaces the pg-meta Docker container behind gen types with the new @supabase/postgrest-typegen package (0.2.0), running introspection and language generation in-process over a direct Postgres connection.

  • New LegacyGenTypesGenerator service seam: the production layer acquires a scoped pg.Pool via legacyAcquirePgPool (full driver parity: TLS mode, DoH resolver, connect-error mapping), feeds it to introspect(), sorts with sortGeneratorMetadata, and renders typescript/go/python/swift.
  • --local connects to the host-mapped db port instead of spawning the pg-meta image inside the stack network; the container inspect stack-running check and rest-version v9 forcing are unchanged. The obsolete .temp/pgmeta-version image override is removed.
  • --db-url now resolves through the shared LegacyDbConfigResolver (libpq keywords, options=reference pooler tenants, sslmode, PG* env fallbacks), matching every other --db-url command. When the DSN carries no explicit sslmode, the existing SSLRequest probe decides whether to connect with sslmode=disable, so plain-TCP servers (common when self-hosting) keep working as they did with pg-meta.
  • Project-ref non-TypeScript paths and the preview-branch fallback keep their Management API flow and IPv4 pooler retry, now classifying the native connect error instead of container stderr. The --linked/--project-id TypeScript path still uses the Management API unchanged.
  • --query-timeout maps to statement_timeout plus the connect timeout; --postgrest-v9-compat disables one-to-one detection in the TypeScript generator; output keeps the trailing newline pg-meta's console.log added.
  • oxfmt (the package's formatter since 0.2.0) resolves its napi binding through createRequire(import.meta.url), which bun build --compile cannot follow, so the CLI embeds the platform binding statically (the @parcel/watcher pattern) and injects it through the generator's format option — verified byte-equivalent to the package default. The never-installed optional prettier plugins oxfmt lazily imports are marked external in both build scripts.

Output parity against postgres-meta 0.98.0 on the same database: Swift byte-identical; Go and Python identical content in canonical sorted order (pg-meta emitted environment-dependent SQL row order); TypeScript identical content with oxfmt's union-wrapping style. Details in the command's SIDE_EFFECTS.md.

Linked issue

Resolves CLI-2279 (no GitHub issue).

  • The linked issue is open and carries the open-for-contribution label (or I'm a Supabase maintainer).

Checklist

  • The PR title follows Conventional Commits (e.g. fix(cli): …).
  • Tests added or updated for the change.
  • From the repository root, pnpm check:all passes; relevant package tests pass for every touched workspace, and pnpm types:check passes for each touched TypeScript workspace (or workspace declaring it).

https://claude.ai/code/session_01RpnbmgTzwdTMkWYF7uFR1V

claude added 2 commits August 31, 2026 11:48
Replace the pg-meta Docker container behind `gen types` with the new
@supabase/postgrest-typegen package, running introspection and language
generation in-process over a direct Postgres connection.

- New LegacyGenTypesGenerator service seam: the production layer acquires
  a scoped pg.Pool via legacyAcquirePgPool (full driver parity: TLS mode,
  DoH resolver, connect-error mapping), feeds it to introspect(), sorts
  with sortGeneratorMetadata, and renders typescript/go/python/swift.
- `--local` connects to the host-mapped db port instead of spawning the
  pg-meta image inside the stack network; the `container inspect`
  stack-running check and rest-version v9 forcing are unchanged. The
  obsolete `.temp/pgmeta-version` image override is removed.
- `--db-url` now resolves through the shared LegacyDbConfigResolver
  (libpq keywords, options=reference pooler tenants, sslmode, PG* env
  fallbacks), matching every other --db-url command.
- Project-ref non-TypeScript paths and the preview-branch fallback keep
  their Management API flow and IPv4 pooler retry, now classifying the
  native connect error instead of container stderr.
- `--query-timeout` maps to statement_timeout plus the connect timeout,
  mirroring the PG_QUERY_TIMEOUT_SECS/PG_CONN_TIMEOUT_SECS envs pg-meta
  received; `--postgrest-v9-compat` disables one-to-one detection in the
  TypeScript generator; output keeps the trailing newline console.log
  added in pg-meta.
- The pg-meta SSL probe, CA bundle templates, and --network-id container
  override are gone with the container; the linked TypeScript path still
  uses the Management API unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpnbmgTzwdTMkWYF7uFR1V
Validated the native typegen end-to-end against a real Postgres 16 and
against postgres-meta 0.98.0 on the same schema, which surfaced two issues:

- The driver requires TLS for remote-looking targets, so `gen types
  --db-url` against a plain-TCP server (common when self-hosting) failed
  where the pg-meta path adapted via its SSL probe. Restore that
  adaptivity in the generator layer: when the DSN carries no explicit
  sslmode, the shared SSLRequest probe decides whether to connect with
  sslmode=disable; probe failures keep the TLS default so the real
  connect error still surfaces.
- prettier 3.5.3 (postgrest-typegen's pin) trips a Bun bundler renaming
  bug under `bun build --compile`, breaking TypeScript generation in the
  compiled binary only. Override it to the repo's prettier 3.9.6, which
  bundles cleanly; pg-meta itself floated ^3.3.3, so there is no
  output-parity concern.

Parity results against postgres-meta on the same database: TypeScript and
Swift byte-identical; Go and Python identical content with canonical
sorted entity ordering (sortGeneratorMetadata) instead of pg-meta's
environment-dependent row order. Documented in SIDE_EFFECTS.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpnbmgTzwdTMkWYF7uFR1V
@avallete
avallete requested a review from a team as a code owner August 31, 2026 14:34
develop's SUPABASE_USE_SLIM_IMAGES change (de133cf) touched gen types only
through resolvePgmetaImage and its tests, all of which this branch deletes
with the pg-meta container path, so the branch side wins in all four files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpnbmgTzwdTMkWYF7uFR1V

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d8c60d5cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/cli/src/legacy/commands/gen/types/types.handler.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts Outdated
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts Outdated
0.2.0 drops prettier for oxfmt, so the prettier bundling override goes
away. oxfmt's ESM dist resolves its napi binding through
createRequire(import.meta.url) and lazily imports optional prettier
plugins — neither survives `bun build --compile` — so the CLI:

- embeds the platform binding statically (the @parcel/watcher pattern:
  one @oxfmt/binding-* devDependency per shipped target, dispatched on
  platform/arch/SUPABASE_LIBC in types.oxfmt.ts) and injects it through
  the generator's new `format` option, verified byte-equivalent to the
  package's own default formatter;
- marks the never-installed optional prettier plugins external in both
  the dev and release build scripts (shared bundle-externals.ts).

Revalidated against a real Postgres 16 from the compiled binary:
Go/Swift/Python output is byte-identical to the 0.1.0 integration;
TypeScript content is identical with oxfmt's union-wrapping style
(three lines differ from the prettier-era output), and source-run vs
compiled-binary output is identical. SIDE_EFFECTS.md parity note
updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpnbmgTzwdTMkWYF7uFR1V
@avallete avallete changed the title refactor(cli): migrate gen types to postgrest-typegen library feat(cli): generate types natively with postgrest-typegen Aug 31, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ebb449be92

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts Outdated
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts Outdated
Comment thread apps/cli/src/legacy/commands/gen/types/SIDE_EFFECTS.md Outdated
Comment thread apps/cli/src/legacy/commands/gen/types/types.errors.ts
avallete and others added 4 commits August 31, 2026 18:06
Keep native typegen SIDE_EFFECTS; drop the slim-image pg-meta notes
develop added, since this command no longer runs that container.

Co-authored-by: Cursor <cursoragent@cursor.com>
Pin the embedded Supabase CA when the SSL probe reports TLS, treat
--query-timeout 0 as disabled rather than an immediate connect
timeout, let the flag override a DSN statement_timeout, bound
introspect() on the client, and classify generator/formatter
failures as internal instead of database findings.

Co-authored-by: Cursor <cursoragent@cursor.com>
A probe error still leaves sslmode unset so the driver default and
IPv6 pooler classification stay intact.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the duplicate TLS-probe comment, unused network-id test wiring,
and a leftover localNetworkId assertion. Clarify that a TLS probe
replaces sslrootcert when sslmode is omitted.

Co-authored-by: Cursor <cursoragent@cursor.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39cb1577fe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.oxfmt.ts Outdated
The glibc and local compile paths already pass oxfmtExternalArgs; musl
release binaries were still resolving those never-installed prettier
plugins and would fail bun build --compile.

Co-authored-by: Cursor <cursoragent@cursor.com>
pull Bot pushed a commit to oogalieboogalie/cli that referenced this pull request Aug 31, 2026
## Summary

Bumps the pinned pg-meta image from `v0.98.0` to `v0.99.0` in the shared
service-image manifest (`apps/cli-go/pkg/config/templates/Dockerfile`,
imported by the TypeScript CLI as its image source).

postgres-meta v0.99.0 replaces the embedded type-generation templates
with the shared
[`@supabase/postgrest-typegen`](https://github.com/supabase/sdk/tree/main/packages/postgrest-typegen)
package (supabase/postgres-meta#1084). This is part of a coordinated
rollout with the hosted path (supabase/platform#37764) so `gen types`
produces the same output locally and via `--project-id`.

## Relationship to supabase#6404

supabase#6404 makes `gen types` run postgrest-typegen in-process, removing the
pg-meta container from that command entirely. This pin still matters
independently of it: the same manifest entry provides the `pgmeta`
service that `supabase start` runs for Studio's local API, and it covers
`gen types` for any release cut before supabase#6404 lands. The two do not
conflict (different files), and output is consistent either way since
v0.99.0 serves the same generator package that supabase#6404 embeds.

## What changes for users

Generated TypeScript output changes in two deliberate ways:
deterministic metadata ordering (a one-time reordering diff when
regenerating existing types) and oxfmt formatting instead of prettier
(style-only). Content is otherwise unchanged.

## Validation

- `go build ./...` passes in both modules.
- The pre-existing `gen types` e2e tests pull this image tag directly,
so CI exercises the new release; the image is published on Docker Hub
and ECR Public.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Supabase CLI preview

npx --yes https://pkg.pr.new/supabase/cli/supabase@8ce5cfa7d6e81bcaef44893236b45b0b18de8bc5

Preview package for commit 8ce5cfa.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 20 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread apps/cli/src/legacy/commands/gen/types/types.handler.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts
@avallete

avallete commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

/ai-review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by a newer AI review

🤖 AI Review

Merged 13 reviewer reports into 12 deduplicated findings. Nine are confirmed, including the IPv4 fallback regression and aggregate introspection timeout; three are refuted by the current implementation and trusted repository conventions.

Findings

Severity Location Category Sources Claim
🟠 MAJOR apps/cli/src/legacy/commands/gen/types/types.handler.ts:389 error-handling claude IPv4 pooler fallback misses common Node IPv6-connectivity failures because the structured driver cause is discarded before classification.
🟠 MAJOR apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts:91 timeout-semantics claude+codex --query-timeout now bounds the complete multi-query introspection operation in addition to each SQL statement, causing cumulatively slow schemas to fail at the default 15 seconds.
🟠 MAJOR apps/cli/src/legacy/commands/gen/types/types.handler.ts:207 behavioral-compatibility claude --network-id and SUPABASE_NETWORK_ID are silently ignored, breaking generation for database hostnames reachable only from the selected Docker network.
🟡 MINOR apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts:30 error-classification claude Operational temporary-directory or disk failures while writing the CA bundle are misclassified as internal CLI bugs.
🟡 MINOR apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts:38 test-coverage claude The production generator lacks focused integration coverage for its remote probe-failure, CA-pinning, and introspection-timeout branches.
🟡 MINOR apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts:78 cancellation codex Interrupting or timing out introspection does not cancel its currently running PostgreSQL query.
🟡 MINOR apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts:106 error-handling codex An exception from the foreign metadata sorter escapes as an Effect defect rather than the declared generation error.
⚪ NIT apps/cli/src/legacy/commands/gen/types/types.integration.test.ts:1937 test-isolation claude The test mutates SUPABASE_DB_PASSWORD before constructing its Effect but restores it only inside that Effect, allowing setup failures to leak the variable.
⚪ NIT apps/cli/src/legacy/commands/gen/types/types.handler.ts:395 output-formatting codex The promised single trailing newline is not enforced; generator output already ending in a newline receives another blank line.
Refuted findings (kept for transparency, not posted as review comments)
  • apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts:57 (behavioral-compatibility): Removing the SUPABASE_CA_SKIP_VERIFY=true warning is an unsanctioned behavior regression.
    Refuted: legacy-pgdelta-ssl-probe.layer.ts:68-74 documents that this wire-level probe never validates certificates, and the native connection now pins the CA at types.generator.layer.ts:62-63. The removed variable therefore no longer controls any operation being performed. Trusted apps/cli/CLAUDE.md also directs intentional behavior changes to update tests and SIDE_EFFECTS rather than add new Go-divergence records, which this PR does.
  • apps/cli/src/legacy/commands/gen/types/types.oxfmt.ts:90 (maintainability): The oxfmt binding and formatter settings can silently drift from postgrest-typegen, and the native binding is repeatedly loaded.
    Refuted: apps/cli/package.json pins postgrest-typegen exactly to 0.2.0 and every binding exactly to 0.65.0; pnpm-lock.yaml confirms that typegen resolves oxfmt 0.65.0. A transitive bump cannot occur without changing the direct typegen pin and lockfile. Repeated require calls also use the module cache rather than reloading the addon.
  • apps/cli/src/legacy/commands/gen/types/types.handler.ts:365 (behavioral-compatibility): The changed --local connection diagnostic requires a separate Go-parity divergence note.
    Refuted: The diagnostic accurately describes the new connection target, types.integration.test.ts:1870 asserts it, and SIDE_EFFECTS.md:60-61 documents the host-mapped connection. Trusted apps/cli/CLAUDE.md says the retired Go implementation is not authoritative and new divergence records must not be added.

Stats

Claude findings: 9 · Codex findings: 4 · Confirmed: 9 · Refuted: 3 · Uncertain: 0


Models: claude-opus-5 + gpt-5.6-sol · Trigger: manual · Workflow run

This review runs once per PR. A maintainer can request another with a /ai-review comment.

Comment thread apps/cli/src/legacy/commands/gen/types/types.handler.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.handler.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.integration.test.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts
Comment thread apps/cli/src/legacy/commands/gen/types/types.handler.ts Outdated
spydon added a commit to supabase/supabase-flutter that referenced this pull request Sep 1, 2026
…#1635)

## What kind of change does this PR introduce?

Feature (draft, layer 2 of the typed table access work, stacked on
#1634). Adds a new `supabase_typegen` package: a standalone code
generator that turns a database schema into the typed table definitions
introduced in #1634, so users get the fully typed surface without
writing any of it by hand.

Linear: SDK-1362

## What is the new behavior?

```sh
supabase gen types --lang dart --local > lib/supabase_schema.g.dart
```

The CLI runs postgrest-typegen's introspection in-process against the
database and hands the language-neutral `GeneratorMetadata` document,
the intermediate representation of
[`@supabase/postgrest-typegen`](https://github.com/supabase/sdk/tree/main/packages/postgrest-typegen)
that its TypeScript, Go, Swift, and Python generators also consume, to
this tool over stdin. The tool emits one Dart file containing, per
table:

- a zero-cost row extension type over the decoded JSON map with typed
getters (`DateTime` parsing, `double`/`num` coercion, `List` casts,
Postgres enum mapping),
- `Insert` and `Update` value extension types that implement
`Map<String, dynamic>`, with required parameters derived from `NOT
NULL`-without-default columns and null-aware omission for everything
else; explicit SQL NULL writes go through generated `set…ToNull` copy
methods that only exist for nullable, writable columns,
- a `PostgrestTable` definition plus `TableColumn` tokens for
compile-time checked filters,
- Dart enums for Postgres enums with wire-name mapping (`toString`
returns the wire name so enum values work directly in filters).

See `packages/supabase_typegen/test/goldens/supabase_schema.dart` for
what the output looks like for the fixture schema.

Design choices worth reviewing:

- **Introspection source**: the `GeneratorMetadata` contract of
`@supabase/postgrest-typegen` (version 1, as shipped in the released
0.2.0 and embedded in postgres-meta v0.99.0). The CLI produces the
document by running the package's `introspect()` in-process against the
local database; there is no postgres-meta dependency. The document comes
straight from the database catalog, so the output is exact where
API-derived descriptions are lossy: `NOT NULL` columns with a database
default read as non-nullable but stay optional on insert, identity
columns are recognized, and `GENERATED ALWAYS` columns appear in the row
type but are excluded from the insert and update types. Structural
validation rejects non-matching documents.
- **Relation and column writability**: tables and foreign tables emit
the full surface; views gate `Insert` and `Update` independently on
`is_insert_enabled` and `is_update_enabled` (falling back to
`is_updatable` for documents predating the flags), so a view writable
only through an INSTEAD OF INSERT trigger gets exactly an insert type;
materialized views are read-only; non-updatable view columns read but
are excluded from writes. This mirrors the TypeScript generator's
semantics.
- **Exact enum resolution**: a column's enum type resolves by its
`type_schema` plus type name, so same-named enums in different schemas
cannot be confused.
- **Canonical ordering**: columns are emitted in the order
`sortGeneratorMetadata` produces (name order within a table), matching
every other postgrest-typegen generator and keeping output insensitive
to column declaration order.
- **Naming**: `books` emits `BooksRow`/`BooksInsert`/`BooksUpdate` plus
a `Books` namespace class (no English singularization, so names stay
predictable). Identifiers are sanitized against Dart reserved words and
`Map` member names with a `$` suffix, and collisions are deduplicated.
- **Lint-clean output**: the emitted code (checked in as a golden)
passes `supabase_lints` and DCM with zero issues, including the strict
extension type rules.

## Additional context

- The metadata fixture is regenerated from a real introspection and
stays reproducible: `test/fixtures/seed.sql` applied to a disposable
Postgres container, introspected with the released
`@supabase/postgrest-typegen@0.2.0` via `tool/regenerate_fixture.ts`.
- CLI exposure as `supabase gen types --lang dart` is a small follow-up
on supabase/cli#6404, which already runs postgrest-typegen's
`introspect()` in-process: the CLI serializes the sorted document and
pipes it to `dart run supabase_typegen` over stdin, the tool's only
input channel. No pg-meta container, no metadata file on disk, and no
user-facing json output language are involved (the earlier
container-based supabase/cli#6230 is closed as superseded).
- The package is excluded from the SDK compliance scan via
`.sdk-parse-ignore` since it is a development-time tool, not SDK client
surface; the symbol, drift and schema checks pass locally against the
base branch.
- `supabase_typegen` is added to the CI dart test matrix; tests are
fully mocked/fixture-based (introspection unit tests over the checked-in
metadata fixture, a whitespace-insensitive golden comparison with a
`tool/regenerate_goldens.dart` refresh script, and behavior tests that
run the generated golden code against a mock HTTP client to verify wire
formats end to end).
- `publish_to: none` until the API settles.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a Dart generator for strongly typed Supabase tables, rows,
inserts, updates, columns, relationships, views, and Postgres enums.
* Added the `supabase_typegen` command-line tool, accepting metadata
through standard input and writing generated code to the terminal or a
file.
* Added safe handling for dates, timestamps, enums, arrays, comments,
and reserved identifiers.

* **Documentation**
* Updated usage guidance, schema-target behavior, generated-code
examples, options, and limitations.

* **Tests**
* Added comprehensive coverage for generation, parsing, serialization,
views, relationships, enums, and typed data access.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
avallete and others added 2 commits September 1, 2026 15:59
The native driver drops errno fields before classification, so IPv4-only
hosts that fail as hostname resolving error (getaddrinfo ENOTFOUND) never
retried through the pooler.

Co-authored-by: Cursor <cursoragent@cursor.com>
CI tsc follows the package `bun` export into postgrest-typegen source, which fails under our noUncheckedIndexedAccess and has no pg-format types.

Co-authored-by: Cursor <cursoragent@cursor.com>
avallete and others added 5 commits September 1, 2026 16:35
Keep develop's bun customConditions for @supabase/config and the pg-topo
paths pin; add the same pin for postgrest-typegen published types.

Co-authored-by: Cursor <cursoragent@cursor.com>
Path-mapping postgrest-typegen to its .d.ts made tsc pass but bun followed
those declaration re-exports and broke compile plus the docs-spec unit test.
Pin typegen only in tsconfig.types.json for types:check.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the pg-format compile workaround now that typegen inlines SQL
literal escaping. Keep source-run oxfmt loading and a single trailing
newline on generated output.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the native typegen dependency and oxfmt bindings. Take develop's
other dep bumps and raise @supabase/pg-delta to 1.0.0-alpha.48 so
db workflows pick up broader managed-schema RLS policy coverage and
the ALTER ROLE search_path render fix.

Co-authored-by: Cursor <cursoragent@cursor.com>
`--query-timeout 1ms` rounded to 0 and silently dropped both timeout
guards. Parse through the shared Go duration helper, refuse rounded-to-0
except explicit disable, and document native TS/Python shape diffs.

Co-authored-by: Cursor <cursoragent@cursor.com>
@avallete

avallete commented Sep 2, 2026

Copy link
Copy Markdown
Member Author

/ai-review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review

The reviews overlap on one major regression: gen types still accepts the Docker network override but no longer honors it, breaking Docker-network-only database hosts. Confirmed additional issues include incorrect filesystem-error telemetry, lost IPv6 fallback classification, an undocumented role change, and missing hermetic coverage for the new generator layer. Three findings were refuted because the behavior is intentional and documented or already supported by tested shared infrastructure.

Findings

Severity Location Category Sources Claim
🟠 MAJOR apps/cli/src/legacy/commands/gen/types/types.handler.ts:321 backward-compatibility claude+codex gen types continues accepting --network-id and SUPABASE_NETWORK_ID but silently ignores them, breaking database hosts reachable only inside the requested Docker network.
🟡 MINOR apps/cli/src/legacy/commands/gen/types/types.errors.ts:58 error-handling claude Failure to write the temporary TLS CA bundle is incorrectly classified as an internal CLI panic rather than an environmental filesystem error.
🟡 MINOR apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts:62 behavior-documentation claude Native remote generation now executes introspection after SET SESSION ROLE postgres for supabase_admin and cli_login_* users, but the command's database-side-effect documentation omits that role change.
🟡 MINOR apps/cli/src/legacy/commands/gen/types/types.generator.layer.ts:44 test-coverage claude The new production generator layer lacks hermetic direct tests for its probe, CA-file, pool-acquisition, and introspection-timeout branches.
🟡 MINOR apps/cli/src/legacy/shared/legacy-connect-errors.ts:477 correctness claude Native EHOSTUNREACH and EADDRNOTAVAIL IPv6 failures no longer trigger the pooler retry because their structured fields are discarded before classification and their rendered messages are not recognized.
⚪ NIT apps/cli/src/legacy/commands/gen/types/types.oxfmt.ts:132 maintainability claude The duplicated oxfmt defaults are not guarded against drift when @<!---->supabase/postgrest-typegen is upgraded.
⚪ NIT apps/cli/src/legacy/commands/gen/types/types.integration.test.ts:428 test-quality claude Most rewritten pooler-fallback tests use a legacy pgconn error string that the new native generator cannot emit.
⚪ NIT AGENTS.md:219 scope claude The PR includes unrelated and partly duplicative agent workflow guidance in the root AGENTS.md.

Findings outside the diff

  • 🟡 MINOR apps/cli/src/legacy/shared/legacy-connect-errors.ts:477 — Native EHOSTUNREACH and EADDRNOTAVAIL IPv6 failures no longer trigger the pooler retry because their structured fields are discarded before classification and their rendered messages are not recognized.
Refuted findings (kept for transparency, not posted as review comments)
  • apps/cli/src/legacy/commands/gen/types/types.shared.ts:35 (parity): Rejecting positive --query-timeout values that round below one second is an unsanctioned compatibility regression.
    Refuted: Concrete code comments, tests, and SIDE_EFFECTS documentation all identify this as an intentional validation rule with an actionable error. Trusted repository guidance requires intentional behavior changes to update tests and SIDE_EFFECTS.md, which this change does; it does not require a special “sanctioned divergence” label.
  • apps/cli/src/legacy/commands/gen/types/types.shared.ts:62 (correctness): Unconditionally setting statement_timeout, including zero, creates an unsupported connection-string and Supavisor-options path.
    Refuted: Zero must overwrite a DSN-provided timeout to implement the documented “flag wins” disable behavior. The shared default-branch connection layer already deliberately supports and tests options=reference=… combined with runtime -c flags, so this is neither a new unsupported form nor evidence of a connection failure.
  • apps/cli/src/legacy/commands/gen/types/SIDE_EFFECTS.md:91 (behavior-change): Removing the SUPABASE_CA_SKIP_VERIFY=true warning leaves a meaningful TLS-verification behavior undocumented.
    Refuted: The environment variable never controls the new probe or connection. The probe only reads the SSLRequest capability byte, and the actual generated TLS connection uses the pinned CA; emitting a warning that the variable “disabled” verification would therefore be inaccurate. The current SIDE_EFFECTS documentation correctly describes the replacement behavior.

Stats

Claude findings: 11 · Codex findings: 1 · Confirmed: 8 · Refuted: 3 · Uncertain: 0


Models: claude-opus-5 + gpt-5.6-sol · Trigger: manual · Workflow run

This review runs once per PR. A maintainer can request another with a /ai-review comment.

* 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")<{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MINOR · error-handling · source: claude

Failure to write the temporary TLS CA bundle is incorrectly classified as an internal CLI panic rather than an environmental filesystem error.

Evidence: types.generator.layer.ts:23-36 maps temporary-directory and file-write failures to LegacyGenTypesGenerateError, while types.errors.ts:54-63 documents that error as a post-introspection generation defect and assigns actionability.internalPanic.

Suggested fix: Use a distinct user-actionable filesystem error classification for CA-bundle materialization failures.

Comment on lines +62 to +65
const pool = yield* legacyAcquirePgPool(conn, {
isLocal: input.isLocal,
dnsResolver: input.dnsResolver,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MINOR · behavior-documentation · source: claude

Native remote generation now executes introspection after SET SESSION ROLE postgres for supabase_admin and cli_login_* users, but the command's database-side-effect documentation omits that role change.

Evidence: types.generator.layer.ts:62-65 uses legacyAcquirePgPool; legacy-db-connection.sql-pg.layer.ts:796-799 and 948-960 apply SET SESSION ROLE postgres remotely. The removed container path only passed PG_META_DB_URL, while SIDE_EFFECTS.md:48-81 describes database access without listing the statement.

Suggested fix: Document the role change in SIDE_EFFECTS.md and confirm that introspection is intended to run with the postgres role's visibility.

Comment on lines +44 to +96
Effect.scoped(
Effect.gen(function* () {
let conn = applyQueryTimeouts(input.conn, input.queryTimeoutSeconds);
// Remote DSNs without sslmode probe first (pg-meta did): no TLS →
// disable; TLS → require + the CA pin pg-meta got via
// PG_META_DB_SSL_ROOT_CERT. Probe failure keeps the driver default.
if (!input.isLocal && conn.sslmode === undefined) {
const probed = yield* sslProbe.requireSslForHost(conn.host, conn.port).pipe(Effect.result);
if (Result.isSuccess(probed)) {
if (!probed.success) {
conn = applyProbedSslMode(conn, false);
} else {
const sslrootcert = yield* pinProbedCaBundle(fs, path);
conn = applyProbedSslMode(conn, true, sslrootcert);
}
}
}

const pool = yield* legacyAcquirePgPool(conn, {
isLocal: input.isLocal,
dnsResolver: input.dnsResolver,
});

// `introspect` drives the injected queryable itself, so the foreign
// Promise boundary is wrapped exactly once here; a live `pg.Pool`
// satisfies its `Queryable` contract directly. `statement_timeout`
// only bounds server-side execution — also cap the client wait so a
// stalled network cannot hang past `--query-timeout`.
const introspectEffect = Effect.tryPromise({
try: () =>
introspect(
pool,
input.includedSchemas.length > 0 ? { includedSchemas: [...input.includedSchemas] } : {},
),
catch: (cause) =>
new LegacyGenTypesMetadataError({
message: `failed to introspect database: ${describeCause(cause)}`,
}),
});
const metadata =
input.queryTimeoutSeconds > 0
? yield* introspectEffect.pipe(
Effect.timeoutOrElse({
duration: Duration.seconds(input.queryTimeoutSeconds),
orElse: () =>
Effect.fail(
new LegacyGenTypesMetadataError({
message: `introspection exceeded --query-timeout ${input.queryTimeoutSeconds}s`,
}),
),
}),
)
: yield* introspectEffect;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MINOR · test-coverage · source: claude

The new production generator layer lacks hermetic direct tests for its probe, CA-file, pool-acquisition, and introspection-timeout branches.

Evidence: The gen/types directory contains no generator-layer test. types.integration.test.ts:145-166 replaces LegacyGenTypesGenerator with a recording fake, while types.unit.test.ts only covers the pure applyProbedSslMode/applyQueryTimeouts helpers. The optional remote e2e at types.e2e.test.ts:369-407 exercises only a live happy path when explicitly enabled.

Suggested fix: Add a hermetic generator-layer test covering TLS accepted/refused/probe-error outcomes, CA-write failure, and client-side introspection timeout.

Comment on lines +132 to +146
/**
* Drop-in for `GenerateTypescriptOptions.format`, byte-equivalent to the
* typegen package's own oxfmt default (same virtual file name, same
* `semi`/`printWidth` options, same error surfacing).
*/
export async function legacyOxfmtTypegenFormat(code: string): Promise<string> {
const binding = legacyRequireOxfmtBinding();
const { code: formatted, errors } = await binding.format(
"output.ts",
code,
{ semi: false, printWidth: 80 },
rejectEmbedded,
rejectEmbedded,
rejectEmbedded,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ NIT · maintainability · source: claude

The duplicated oxfmt defaults are not guarded against drift when @<!---->supabase/postgrest-typegen is upgraded.

Evidence: types.oxfmt.ts:132-146 hard-codes output.ts, semi=false, and printWidth=80. types.unit.test.ts:165-184 only verifies binding dependency versions and never compares formatting with the package's default formatter.

Suggested fix: Add a fixture test comparing the injected formatter with postgrest-typegen's default output, or share the default options.

Comment on lines +321 to 345
const runTypegen = (input: {
readonly conn: LegacyPgConnInput;
readonly isLocal: boolean;
readonly includedSchemas: ReadonlyArray<string>;
readonly postgrestV9Compat: boolean;
readonly pgmetaVersionOverride?: string;
readonly poolerFallback?: {
readonly directHost: string;
readonly eligible: boolean;
readonly resolve: Effect.Effect<Option.Option<LegacyPgConnInput>, unknown>;
};
}) =>
Effect.scoped(
Effect.gen(function* () {
const buildRun = (target: {
readonly url: string;
readonly host: string;
readonly port: number;
readonly probeHost: string;
readonly probePort: number;
}) =>
Effect.gen(function* () {
yield* output.raw(`Connecting to ${target.host} ${target.port}\n`, "stderr");

// Each entry is a "KEY=VALUE" string, passed as a `--env
// KEY=VALUE` argument rather than a `--env-file`: env-files
// split on newlines, so they cannot carry the multi-line PEM CA
// bundle, and a value containing a newline could inject an extra
// variable. Passing argv elements keeps each entry as exactly
// one variable regardless of its contents.
const env = [
`PG_META_DB_URL=${target.url}`,
`PG_CONN_TIMEOUT_SECS=${queryTimeoutSeconds}`,
`PG_QUERY_TIMEOUT_SECS=${queryTimeoutSeconds}`,
`PG_META_GENERATE_TYPES=${lang}`,
`PG_META_GENERATE_TYPES_INCLUDED_SCHEMAS=${input.includedSchemas}`,
`PG_META_GENERATE_TYPES_SWIFT_ACCESS_CONTROL=${swiftAccessControl}`,
`PG_META_GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS=${String(!input.postgrestV9Compat)}`,
];

// Emitted to stderr when the probe runs with certificate
// verification disabled. Our wire-level SSLRequest probe never
// verifies certificates, so honour the same env var here too.
if (process.env["SUPABASE_CA_SKIP_VERIFY"] === "true") {
yield* output.raw(
"WARNING: TLS certificate verification disabled for SSL probe (SUPABASE_CA_SKIP_VERIFY=true)\n",
"stderr",
);
}

const useTls = yield* sslProbe.requireSslForHost(target.probeHost, target.probePort);
if (useTls) {
env.push(`PG_META_DB_SSL_ROOT_CERT=${legacyRootCaBundle()}`);
}

// `--network-id` overrides any base network mode (even the
// "host" mode used for --db-url), so honour the override here too.
const networkMode = Option.isSome(networkId) ? networkId.value : input.networkMode;
const pgmetaImage = resolvePgmetaImage(input.pgmetaVersionOverride);
const args = [
"run",
"--rm",
"--network",
networkMode,
...env.flatMap((entry) => ["--env", entry]),
pgmetaImage,
"node",
"dist/server/server.js",
];
const child = yield* spawnContainerCli(spawner, args, {
stdin: "ignore",
stdout: "pipe",
stderr: "pipe",
});

let stderrText = "";
const [exitCode] = yield* Effect.all(
[
child.exitCode.pipe(Effect.map(Number)),
forwardByteStream(child.stdout, (text) => output.raw(text, "stdout")),
forwardByteStream(child.stderr, (text) =>
Effect.sync(() => {
stderrText += text;
}).pipe(Effect.andThen(output.raw(text, "stderr"))),
),
],
{ concurrency: "unbounded" },
);
return { exitCode, stderrText };
Effect.gen(function* () {
const generateTarget = (conn: LegacyPgConnInput, isLocal: boolean) =>
Effect.gen(function* () {
yield* output.raw(`Connecting to ${conn.host} ${conn.port}\n`, "stderr");
return yield* generator.generate({
conn,
isLocal,
dnsResolver,
lang,
includedSchemas: input.includedSchemas,
postgrestV9Compat: input.postgrestV9Compat,
swiftAccessControl,
queryTimeoutSeconds,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 MAJOR · backward-compatibility · source: claude+codex

gen types continues accepting --network-id and SUPABASE_NETWORK_ID but silently ignores them, breaking database hosts reachable only inside the requested Docker network.

Evidence: types.handler.ts:321-345 passes no network setting to native generation, while shared/legacy/global-flags.ts:73-77 still exposes --network-id. The diff removes the handler's LegacyNetworkIdFlag read and previous Docker --network override; SIDE_EFFECTS.md:78-81 acknowledges that such hostnames no longer resolve.

Suggested fix: Use a container-backed path when a network override is supplied, or reject the override with a clear actionable error.

Comment on lines +428 to 435
const IPV6_CONNECT_FAILURE = new LegacyDbConnectError({
message: `failed to connect to postgres: could not translate host name "db.${LEGACY_VALID_REF}.supabase.co" to address: No address associated with hostname`,
});

try {
return await run(address.port);
} finally {
await new Promise<void>((resolve, reject) =>
server.close((error) => (error ? reject(error) : resolve())),
);
}
}
const NATIVE_ENOTFOUND_CONNECT_FAILURE = new LegacyDbConnectError({
message: `failed to connect to postgres: failed to connect to \`host=db.${LEGACY_VALID_REF}.supabase.co user=postgres database=postgres\`: hostname resolving error (getaddrinfo ENOTFOUND)`,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ NIT · test-quality · source: claude

Most rewritten pooler-fallback tests use a legacy pgconn error string that the new native generator cannot emit.

Evidence: types.integration.test.ts:428-430 defines IPV6_CONNECT_FAILURE using could not translate host name; it is used at lines 1139, 1312, 1351, 1390, 1560, 1619, and 1838. The native-shaped fixture at lines 432-434 is used only at line 1235, while legacyConnectCauseDetail renders native DNS failures as hostname resolving error (...).

Suggested fix: Make the shared fixture use a native driver error shape and retain a separate legacy-text case only where backward-tolerant classification is intentionally tested.

Comment thread AGENTS.md
Comment on lines +219 to +220
Never `git commit` or `git push` until lint and `types:check` have been run and passed for the change. Targeted unit/integration tests are not a substitute — CI Check code quality runs `pnpm check:all` (`types:check`, oxlint, oxfmt, knip). Before commit or push, from each changed TypeScript workspace run `pnpm types:check`, and from the repo root run `pnpm exec oxlint` (or `pnpm check:all`). If those fail, fix them before committing.
After every `git push` to a branch that has a PR, check GitHub CI for that PR (`gh pr checks` / `gh run list`) and report whether it is green. If it is not, diagnose and fix; do not leave a red PR as done.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ NIT · scope · source: claude

The PR includes unrelated and partly duplicative agent workflow guidance in the root AGENTS.md.

Evidence: AGENTS.md:219-220 adds commit/push and GitHub-CI instructions immediately before the existing lines 222-224 requiring relevant checks to pass and unresolved failures to be fixed. The gen-types implementation does not depend on this guidance.

Suggested fix: Move the workflow-policy change to a separate PR and consolidate it with the existing quality-check section.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants