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
50 changes: 50 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@sentry/node": "^9.12.0",
"@sentry/profiling-node": "^9.12.0",
"@sinclair/typebox": "^0.31.25",
"@supabase/postgrest-typegen": "https://pkg.pr.new/supabase/pg-toolbelt/@supabase/postgrest-typegen@11ff444",
"close-with-grace": "^2.1.0",
"crypto-js": "^4.0.0",
"fastify": "^4.24.3",
Expand Down
175 changes: 46 additions & 129 deletions src/lib/generators.ts
Original file line number Diff line number Diff line change
@@ -1,141 +1,58 @@
import PostgresMeta from './PostgresMeta.js'
import {
PostgresColumn,
PostgresForeignTable,
PostgresFunction,
PostgresMaterializedView,
PostgresMetaResult,
PostgresRelationship,
PostgresSchema,
PostgresTable,
PostgresType,
PostgresView,
} from './types.js'

export type GeneratorMetadata = {
schemas: PostgresSchema[]
tables: Omit<PostgresTable, 'columns'>[]
foreignTables: Omit<PostgresForeignTable, 'columns'>[]
views: Omit<PostgresView, 'columns'>[]
materializedViews: Omit<PostgresMaterializedView, 'columns'>[]
columns: PostgresColumn[]
relationships: PostgresRelationship[]
functions: PostgresFunction[]
types: PostgresType[]
}

introspect,
sortGeneratorMetadata,
type GeneratorMetadata,
type Queryable,
} from '@supabase/postgrest-typegen'
import PostgresMeta from './PostgresMeta.js'
import { PostgresMetaResult } from './types.js'

// Re-export so existing consumers can keep importing the type from here.
export type { GeneratorMetadata }

/**
* Adapter over `@supabase/postgrest-typegen`'s `introspect()`, preserving the
* historical `getGeneratorMetadata` signature and `{ data, error }` contract.
*
* The package is driver-agnostic: it takes a structural `Queryable` whose
* `query()` resolves to `{ rows }` and throws on failure. We wrap `pgMeta.query`
* (which returns `{ data, error }`) into that shape, surface the first query
* error as the result error, and always end the pool — matching the previous
* behavior.
*/
export async function getGeneratorMetadata(
pgMeta: PostgresMeta,
filters: { includedSchemas?: string[]; excludedSchemas?: string[] } = {
includedSchemas: [],
excludedSchemas: [],
}
): Promise<PostgresMetaResult<GeneratorMetadata>> {
const includedSchemas = filters.includedSchemas ?? []
const excludedSchemas = filters.excludedSchemas ?? []

const { data: schemas, error: schemasError } = await pgMeta.schemas.list({
includeSystemSchemas: false,
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
excludedSchemas: excludedSchemas.length > 0 ? excludedSchemas : undefined,
})
if (schemasError) {
return { data: null, error: schemasError }
}

const { data: tables, error: tablesError } = await pgMeta.tables.list({
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
excludedSchemas: excludedSchemas.length > 0 ? excludedSchemas : undefined,
includeColumns: false,
})
if (tablesError) {
return { data: null, error: tablesError }
}

const { data: foreignTables, error: foreignTablesError } = await pgMeta.foreignTables.list({
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
excludedSchemas: excludedSchemas.length > 0 ? excludedSchemas : undefined,
includeColumns: false,
})
if (foreignTablesError) {
return { data: null, error: foreignTablesError }
}

const { data: views, error: viewsError } = await pgMeta.views.list({
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
excludedSchemas: excludedSchemas.length > 0 ? excludedSchemas : undefined,
includeColumns: false,
})
if (viewsError) {
return { data: null, error: viewsError }
}

const { data: materializedViews, error: materializedViewsError } =
await pgMeta.materializedViews.list({
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
excludedSchemas: excludedSchemas.length > 0 ? excludedSchemas : undefined,
includeColumns: false,
})
if (materializedViewsError) {
return { data: null, error: materializedViewsError }
}

const { data: columns, error: columnsError } = await pgMeta.columns.list({
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
excludedSchemas: excludedSchemas.length > 0 ? excludedSchemas : undefined,
includeSystemSchemas: false,
})
if (columnsError) {
return { data: null, error: columnsError }
}

const { data: relationships, error: relationshipsError } = await pgMeta.relationships.list({
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
excludedSchemas: excludedSchemas.length > 0 ? excludedSchemas : undefined,
includeSystemSchemas: false,
})
if (relationshipsError) {
return { data: null, error: relationshipsError }
}

const { data: functions, error: functionsError } = await pgMeta.functions.list({
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
excludedSchemas: excludedSchemas.length > 0 ? excludedSchemas : undefined,
includeSystemSchemas: false,
})
if (functionsError) {
return { data: null, error: functionsError }
}

const { data: types, error: typesError } = await pgMeta.types.list({
includeTableTypes: true,
includeArrayTypes: true,
includeSystemSchemas: true,
})
if (typesError) {
return { data: null, error: typesError }
const queryable: Queryable = {
query: async (sql: string) => {
const { data, error } = await pgMeta.query(sql)
if (error) {
throw error
}
return { rows: data ?? [] }
},
}

await pgMeta.end()

return {
data: {
schemas: schemas.filter(
({ name }) =>
!excludedSchemas.includes(name) &&
(includedSchemas.length === 0 || includedSchemas.includes(name))
),
tables,
foreignTables,
views,
materializedViews,
columns,
relationships,
functions: functions.filter(
({ return_type }) => !['trigger', 'event_trigger'].includes(return_type)
),
types,
},
error: null,
try {
// The generators emit objects in metadata order, so apply the package's
// canonical sort pass before returning (and before any generator runs).
const data = sortGeneratorMetadata(
await introspect(queryable, {
includedSchemas: filters.includedSchemas,
excludedSchemas: filters.excludedSchemas,
})
)
return { data, error: null }
} catch (error) {
return {
data: null,
error: error as PostgresMetaResult<GeneratorMetadata>['error'] & { message: string },
}
} finally {
await pgMeta.end()
}
}
6 changes: 1 addition & 5 deletions src/server/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import crypto from 'crypto'
import { PoolConfig } from '../lib/types.js'
import { getSecret } from '../lib/secrets.js'
import { AccessControl } from './templates/swift.js'
import type { AccessControl } from '@supabase/postgrest-typegen'
import pkg from '#package.json' with { type: 'json' }

export const PG_META_HOST = process.env.PG_META_HOST || '0.0.0.0'
Expand Down Expand Up @@ -51,10 +51,6 @@ export const GENERATE_TYPES_SWIFT_ACCESS_CONTROL = process.env
? (process.env.PG_META_GENERATE_TYPES_SWIFT_ACCESS_CONTROL as AccessControl)
: 'internal'

// json/jsonb/text types
export const VALID_UNNAMED_FUNCTION_ARG_TYPES = new Set([114, 3802, 25])
export const VALID_FUNCTION_ARGS_MODE = new Set(['in', 'inout', 'variadic'])

export const PG_META_MAX_RESULT_SIZE = process.env.PG_META_MAX_RESULT_SIZE_MB
? // Node-postgres get a maximum size in bytes make the conversion from the env variable
// from MB to Bytes
Expand Down
4 changes: 2 additions & 2 deletions src/server/routes/generators/go.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FastifyInstance } from 'fastify'
import { PostgresMeta } from '../../../lib/index.js'
import { createConnectionConfig, extractRequestForLogging } from '../../utils.js'
import { apply as applyGoTemplate } from '../../templates/go.js'
import { generateGo } from '@supabase/postgrest-typegen'
import { getGeneratorMetadata } from '../../../lib/generators.js'

export default async (fastify: FastifyInstance) => {
Expand Down Expand Up @@ -29,6 +29,6 @@ export default async (fastify: FastifyInstance) => {
return { error: generatorMetaError.message }
}

return applyGoTemplate(generatorMeta)
return generateGo(generatorMeta!)
})
}
4 changes: 2 additions & 2 deletions src/server/routes/generators/python.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FastifyInstance } from 'fastify'
import { PostgresMeta } from '../../../lib/index.js'
import { createConnectionConfig, extractRequestForLogging } from '../../utils.js'
import { apply as applyPyTemplate } from '../../templates/python.js'
import { generatePython } from '@supabase/postgrest-typegen'
import { getGeneratorMetadata } from '../../../lib/generators.js'

export default async (fastify: FastifyInstance) => {
Expand All @@ -28,6 +28,6 @@ export default async (fastify: FastifyInstance) => {
return { error: generatorMetaError.message }
}

return applyPyTemplate(generatorMeta)
return generatePython(generatorMeta!)
})
}
7 changes: 2 additions & 5 deletions src/server/routes/generators/swift.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FastifyInstance } from 'fastify'
import { PostgresMeta } from '../../../lib/index.js'
import { createConnectionConfig, extractRequestForLogging } from '../../utils.js'
import { apply as applySwiftTemplate, AccessControl } from '../../templates/swift.js'
import { type AccessControl, generateSwift } from '@supabase/postgrest-typegen'
import { getGeneratorMetadata } from '../../../lib/generators.js'

export default async (fastify: FastifyInstance) => {
Expand Down Expand Up @@ -31,9 +31,6 @@ export default async (fastify: FastifyInstance) => {
return { error: generatorMetaError.message }
}

return applySwiftTemplate({
...generatorMeta,
accessControl,
})
return generateSwift(generatorMeta!, { accessControl })
})
}
5 changes: 2 additions & 3 deletions src/server/routes/generators/typescript.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FastifyInstance } from 'fastify'
import { PostgresMeta } from '../../../lib/index.js'
import { createConnectionConfig, extractRequestForLogging } from '../../utils.js'
import { apply as applyTypescriptTemplate } from '../../templates/typescript.js'
import { generateTypescript } from '@supabase/postgrest-typegen'
import { getGeneratorMetadata } from '../../../lib/generators.js'

export default async (fastify: FastifyInstance) => {
Expand Down Expand Up @@ -33,8 +33,7 @@ export default async (fastify: FastifyInstance) => {
return { error: generatorMetaError.message }
}

return applyTypescriptTemplate({
...generatorMeta,
return generateTypescript(generatorMeta!, {
detectOneToOneRelationships,
postgrestVersion,
})
Expand Down
Loading