diff --git a/graphile/node-type-registry/src/codegen/generate-types.ts b/graphile/node-type-registry/src/codegen/generate-types.ts
index 88d6082c8..f9aa480b3 100644
--- a/graphile/node-type-registry/src/codegen/generate-types.ts
+++ b/graphile/node-type-registry/src/codegen/generate-types.ts
@@ -14,7 +14,7 @@
* when building blueprint JSON. The API itself accepts plain JSONB.
*
* Usage:
- * npx ts-node src/codegen/generate-types.ts [--outdir
] [--introspection ]
+ * npx ts-node src/codegen/generate-types.ts [--outdir ] [--meta ]
*/
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -29,10 +29,10 @@ import { allNodeTypes } from '../index';
import type { NodeTypeDefinition } from '../types';
// ---------------------------------------------------------------------------
-// Introspection types (subset of TableMeta from graphile-settings)
+// _meta types (matches TableMeta / FieldMeta from MetaSchemaPlugin)
// ---------------------------------------------------------------------------
-interface IntrospectionFieldMeta {
+interface MetaFieldInfo {
name: string;
type: { pgType: string; gqlType: string; isArray: boolean };
isNotNull: boolean;
@@ -42,10 +42,10 @@ interface IntrospectionFieldMeta {
description: string | null;
}
-interface IntrospectionTableMeta {
+interface MetaTableInfo {
name: string;
schemaName: string;
- fields: IntrospectionFieldMeta[];
+ fields: MetaFieldInfo[];
}
// ---------------------------------------------------------------------------
@@ -262,7 +262,7 @@ function pgTypeToTSType(pgType: string, isArray: boolean): t.TSType {
* `typeOverrides` for columns that need a non-default TS type.
*/
function deriveInterfaceFromTable(
- table: IntrospectionTableMeta,
+ table: MetaTableInfo,
interfaceName: string,
description: string,
typeOverrides?: Record
@@ -291,22 +291,22 @@ function deriveInterfaceFromTable(
// ---------------------------------------------------------------------------
function findTable(
- tables: IntrospectionTableMeta[],
+ tables: MetaTableInfo[],
schemaName: string,
tableName: string
-): IntrospectionTableMeta | undefined {
+): MetaTableInfo | undefined {
return tables.find((tbl) => tbl.schemaName === schemaName && tbl.name === tableName);
}
function buildBlueprintField(
- introspection?: IntrospectionTableMeta[]
+ meta?: MetaTableInfo[]
): t.ExportNamedDeclaration {
- const table = introspection && findTable(introspection, 'metaschema_public', 'field');
+ const table = meta && findTable(meta, 'metaschema_public', 'field');
if (table) {
return deriveInterfaceFromTable(
table,
'BlueprintField',
- 'A custom field (column) to add to a blueprint table. Derived from metaschema_public.field.',
+ 'A custom field (column) to add to a blueprint table. Derived from _meta.',
);
}
// Static fallback
@@ -324,19 +324,19 @@ function buildBlueprintField(
function buildBlueprintPolicy(
authzNodes: NodeTypeDefinition[],
- introspection?: IntrospectionTableMeta[]
+ meta?: MetaTableInfo[]
): t.ExportNamedDeclaration {
const policyTypeAnnotation =
authzNodes.length > 0
? strUnion(authzNodes.map((nt) => nt.name))
: t.tsStringKeyword();
- const table = introspection && findTable(introspection, 'metaschema_public', 'policy');
+ const table = meta && findTable(meta, 'metaschema_public', 'policy');
if (table) {
return deriveInterfaceFromTable(
table,
'BlueprintPolicy',
- 'An RLS policy entry for a blueprint table. Derived from metaschema_public.policy.',
+ 'An RLS policy entry for a blueprint table. Derived from _meta.',
{
// policy_type gets a typed union of known Authz* node names
policy_type: policyTypeAnnotation,
@@ -403,14 +403,14 @@ function buildBlueprintFullTextSearch(): t.ExportNamedDeclaration {
}
function buildBlueprintIndex(
- introspection?: IntrospectionTableMeta[]
+ meta?: MetaTableInfo[]
): t.ExportNamedDeclaration {
- const table = introspection && findTable(introspection, 'metaschema_public', 'index');
+ const table = meta && findTable(meta, 'metaschema_public', 'index');
if (table) {
return deriveInterfaceFromTable(
table,
'BlueprintIndex',
- 'An index definition within a blueprint. Derived from metaschema_public.index.',
+ 'An index definition within a blueprint. Derived from _meta.',
{
// JSONB columns get Record instead of the default
index_params: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
@@ -634,7 +634,7 @@ function sectionComment(title: string): t.Statement {
// Main generator
// ---------------------------------------------------------------------------
-function buildProgram(introspection?: IntrospectionTableMeta[]): string {
+function buildProgram(meta?: MetaTableInfo[]): string {
const statements: t.Statement[] = [];
// Group node types by category
@@ -667,16 +667,16 @@ function buildProgram(introspection?: IntrospectionTableMeta[]): string {
statements.push(...generateParamsInterfaces(nts));
}
- // -- Structural types (introspection-driven when available) --
- const introspectionSource = introspection
- ? 'Derived from introspection JSON'
- : 'Static fallback (no introspection provided)';
- statements.push(sectionComment(`Structural types — ${introspectionSource}`));
- statements.push(buildBlueprintField(introspection));
- statements.push(buildBlueprintPolicy(authzNodes, introspection));
+ // -- Structural types (_meta-driven when available, static fallback otherwise) --
+ const metaSource = meta
+ ? 'Derived from _meta'
+ : 'Static fallback (no _meta provided)';
+ statements.push(sectionComment(`Structural types — ${metaSource}`));
+ statements.push(buildBlueprintField(meta));
+ statements.push(buildBlueprintPolicy(authzNodes, meta));
statements.push(buildBlueprintFtsSource());
statements.push(buildBlueprintFullTextSearch());
- statements.push(buildBlueprintIndex(introspection));
+ statements.push(buildBlueprintIndex(meta));
// -- Node types discriminated union --
statements.push(
@@ -725,19 +725,25 @@ function main() {
const outdir =
outdirIdx !== -1 ? args[outdirIdx + 1] : join(__dirname, '..');
- const introspectionIdx = args.indexOf('--introspection');
- let introspection: IntrospectionTableMeta[] | undefined;
- if (introspectionIdx !== -1 && args[introspectionIdx + 1]) {
- const introspectionPath = args[introspectionIdx + 1];
- console.log(`Reading introspection from ${introspectionPath}`);
- const raw = readFileSync(introspectionPath, 'utf-8');
- introspection = JSON.parse(raw) as IntrospectionTableMeta[];
- console.log(`Loaded ${introspection.length} tables from introspection`);
+ const metaIdx = args.indexOf('--meta');
+ let meta: MetaTableInfo[] | undefined;
+ if (metaIdx !== -1 && args[metaIdx + 1]) {
+ const metaPath = args[metaIdx + 1];
+ console.log(`Reading _meta from ${metaPath}`);
+ const raw = readFileSync(metaPath, 'utf-8');
+ const parsed = JSON.parse(raw);
+ // Accept both { tables: [...] } (GQL query result) and raw [...] (array)
+ meta = (Array.isArray(parsed) ? parsed : parsed?.tables ?? parsed?.data?._meta?.tables) as MetaTableInfo[] | undefined;
+ if (meta) {
+ console.log(`Loaded ${meta.length} tables from _meta`);
+ } else {
+ console.log('Could not find tables in _meta JSON; using static fallback types');
+ }
} else {
- console.log('No --introspection flag; using static fallback types');
+ console.log('No --meta flag; using static fallback types');
}
- const content = buildProgram(introspection);
+ const content = buildProgram(meta);
const filename = 'blueprint-types.generated.ts';
const filepath = join(outdir, filename);
diff --git a/graphql/codegen/src/core/codegen/cli/table-command-generator.ts b/graphql/codegen/src/core/codegen/cli/table-command-generator.ts
index c44af1578..a121a5595 100644
--- a/graphql/codegen/src/core/codegen/cli/table-command-generator.ts
+++ b/graphql/codegen/src/core/codegen/cli/table-command-generator.ts
@@ -547,7 +547,7 @@ function buildSearchHandler(
const whereProps: t.ObjectProperty[] = [];
for (const group of specialGroups) {
for (const field of group.fields) {
- if (field.type.pgType?.toLowerCase() === 'tsvector') {
+ if (field.type.gqlType === 'FullText' && !field.type.isArray) {
// tsvector field: { query }
whereProps.push(
t.objectProperty(
diff --git a/graphql/codegen/src/core/codegen/docs-utils.ts b/graphql/codegen/src/core/codegen/docs-utils.ts
index 33c126328..46ccdd1fa 100644
--- a/graphql/codegen/src/core/codegen/docs-utils.ts
+++ b/graphql/codegen/src/core/codegen/docs-utils.ts
@@ -153,15 +153,20 @@ function isPostGISField(f: Field): boolean {
}
function isEmbeddingField(f: Field): boolean {
- const pgType = f.type.pgType?.toLowerCase();
- if (pgType === 'vector') return true;
+ // VectorCodecPlugin maps pgvector `vector` columns to the `Vector` GQL scalar.
+ // This is the primary detection path — no _meta or pgType enrichment needed.
+ if (f.type.gqlType === 'Vector') return true;
+ // Legacy fallback: name-based heuristic for schemas without VectorCodecPlugin
if (/embedding$/i.test(f.name) && f.type.isArray && f.type.gqlType === 'Float') return true;
return false;
}
function isTsvectorField(f: Field): boolean {
const pgType = f.type.pgType?.toLowerCase();
- return pgType === 'tsvector';
+ if (pgType === 'tsvector') return true;
+ // Fallback: PostGraphile maps tsvector columns to the FullText GQL scalar
+ if (f.type.gqlType === 'FullText' && !f.type.isArray) return true;
+ return false;
}
function isSearchComputedField(f: Field): boolean {