Skip to content

Commit a65b33d

Browse files
committed
refactor(node-type-registry): replace --introspection with --meta for blueprint type generation
Customers without direct Postgres access can now use the _meta GraphQL query output to generate typed blueprint interfaces. The --meta flag accepts _meta.json in any format: raw TableMeta[], { tables: [...] }, or { data: { _meta: { tables: [...] } } }. When --meta is not provided, static fallback types are used (no breakage). Renames IntrospectionFieldMeta/IntrospectionTableMeta to MetaFieldInfo/ MetaTableInfo to align with MetaSchemaPlugin naming.
1 parent 69531c9 commit a65b33d

1 file changed

Lines changed: 42 additions & 36 deletions

File tree

graphile/node-type-registry/src/codegen/generate-types.ts

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* when building blueprint JSON. The API itself accepts plain JSONB.
1515
*
1616
* Usage:
17-
* npx ts-node src/codegen/generate-types.ts [--outdir <dir>] [--introspection <path>]
17+
* npx ts-node src/codegen/generate-types.ts [--outdir <dir>] [--meta <path>]
1818
*/
1919

2020
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -29,10 +29,10 @@ import { allNodeTypes } from '../index';
2929
import type { NodeTypeDefinition } from '../types';
3030

3131
// ---------------------------------------------------------------------------
32-
// Introspection types (subset of TableMeta from graphile-settings)
32+
// _meta types (matches TableMeta / FieldMeta from MetaSchemaPlugin)
3333
// ---------------------------------------------------------------------------
3434

35-
interface IntrospectionFieldMeta {
35+
interface MetaFieldInfo {
3636
name: string;
3737
type: { pgType: string; gqlType: string; isArray: boolean };
3838
isNotNull: boolean;
@@ -42,10 +42,10 @@ interface IntrospectionFieldMeta {
4242
description: string | null;
4343
}
4444

45-
interface IntrospectionTableMeta {
45+
interface MetaTableInfo {
4646
name: string;
4747
schemaName: string;
48-
fields: IntrospectionFieldMeta[];
48+
fields: MetaFieldInfo[];
4949
}
5050

5151
// ---------------------------------------------------------------------------
@@ -262,7 +262,7 @@ function pgTypeToTSType(pgType: string, isArray: boolean): t.TSType {
262262
* `typeOverrides` for columns that need a non-default TS type.
263263
*/
264264
function deriveInterfaceFromTable(
265-
table: IntrospectionTableMeta,
265+
table: MetaTableInfo,
266266
interfaceName: string,
267267
description: string,
268268
typeOverrides?: Record<string, t.TSType>
@@ -291,22 +291,22 @@ function deriveInterfaceFromTable(
291291
// ---------------------------------------------------------------------------
292292

293293
function findTable(
294-
tables: IntrospectionTableMeta[],
294+
tables: MetaTableInfo[],
295295
schemaName: string,
296296
tableName: string
297-
): IntrospectionTableMeta | undefined {
297+
): MetaTableInfo | undefined {
298298
return tables.find((tbl) => tbl.schemaName === schemaName && tbl.name === tableName);
299299
}
300300

301301
function buildBlueprintField(
302-
introspection?: IntrospectionTableMeta[]
302+
meta?: MetaTableInfo[]
303303
): t.ExportNamedDeclaration {
304-
const table = introspection && findTable(introspection, 'metaschema_public', 'field');
304+
const table = meta && findTable(meta, 'metaschema_public', 'field');
305305
if (table) {
306306
return deriveInterfaceFromTable(
307307
table,
308308
'BlueprintField',
309-
'A custom field (column) to add to a blueprint table. Derived from metaschema_public.field.',
309+
'A custom field (column) to add to a blueprint table. Derived from _meta.',
310310
);
311311
}
312312
// Static fallback
@@ -324,19 +324,19 @@ function buildBlueprintField(
324324

325325
function buildBlueprintPolicy(
326326
authzNodes: NodeTypeDefinition[],
327-
introspection?: IntrospectionTableMeta[]
327+
meta?: MetaTableInfo[]
328328
): t.ExportNamedDeclaration {
329329
const policyTypeAnnotation =
330330
authzNodes.length > 0
331331
? strUnion(authzNodes.map((nt) => nt.name))
332332
: t.tsStringKeyword();
333333

334-
const table = introspection && findTable(introspection, 'metaschema_public', 'policy');
334+
const table = meta && findTable(meta, 'metaschema_public', 'policy');
335335
if (table) {
336336
return deriveInterfaceFromTable(
337337
table,
338338
'BlueprintPolicy',
339-
'An RLS policy entry for a blueprint table. Derived from metaschema_public.policy.',
339+
'An RLS policy entry for a blueprint table. Derived from _meta.',
340340
{
341341
// policy_type gets a typed union of known Authz* node names
342342
policy_type: policyTypeAnnotation,
@@ -403,14 +403,14 @@ function buildBlueprintFullTextSearch(): t.ExportNamedDeclaration {
403403
}
404404

405405
function buildBlueprintIndex(
406-
introspection?: IntrospectionTableMeta[]
406+
meta?: MetaTableInfo[]
407407
): t.ExportNamedDeclaration {
408-
const table = introspection && findTable(introspection, 'metaschema_public', 'index');
408+
const table = meta && findTable(meta, 'metaschema_public', 'index');
409409
if (table) {
410410
return deriveInterfaceFromTable(
411411
table,
412412
'BlueprintIndex',
413-
'An index definition within a blueprint. Derived from metaschema_public.index.',
413+
'An index definition within a blueprint. Derived from _meta.',
414414
{
415415
// JSONB columns get Record<string, unknown> instead of the default
416416
index_params: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
@@ -634,7 +634,7 @@ function sectionComment(title: string): t.Statement {
634634
// Main generator
635635
// ---------------------------------------------------------------------------
636636

637-
function buildProgram(introspection?: IntrospectionTableMeta[]): string {
637+
function buildProgram(meta?: MetaTableInfo[]): string {
638638
const statements: t.Statement[] = [];
639639

640640
// Group node types by category
@@ -667,16 +667,16 @@ function buildProgram(introspection?: IntrospectionTableMeta[]): string {
667667
statements.push(...generateParamsInterfaces(nts));
668668
}
669669

670-
// -- Structural types (introspection-driven when available) --
671-
const introspectionSource = introspection
672-
? 'Derived from introspection JSON'
673-
: 'Static fallback (no introspection provided)';
674-
statements.push(sectionComment(`Structural types — ${introspectionSource}`));
675-
statements.push(buildBlueprintField(introspection));
676-
statements.push(buildBlueprintPolicy(authzNodes, introspection));
670+
// -- Structural types (_meta-driven when available, static fallback otherwise) --
671+
const metaSource = meta
672+
? 'Derived from _meta'
673+
: 'Static fallback (no _meta provided)';
674+
statements.push(sectionComment(`Structural types — ${metaSource}`));
675+
statements.push(buildBlueprintField(meta));
676+
statements.push(buildBlueprintPolicy(authzNodes, meta));
677677
statements.push(buildBlueprintFtsSource());
678678
statements.push(buildBlueprintFullTextSearch());
679-
statements.push(buildBlueprintIndex(introspection));
679+
statements.push(buildBlueprintIndex(meta));
680680

681681
// -- Node types discriminated union --
682682
statements.push(
@@ -725,19 +725,25 @@ function main() {
725725
const outdir =
726726
outdirIdx !== -1 ? args[outdirIdx + 1] : join(__dirname, '..');
727727

728-
const introspectionIdx = args.indexOf('--introspection');
729-
let introspection: IntrospectionTableMeta[] | undefined;
730-
if (introspectionIdx !== -1 && args[introspectionIdx + 1]) {
731-
const introspectionPath = args[introspectionIdx + 1];
732-
console.log(`Reading introspection from ${introspectionPath}`);
733-
const raw = readFileSync(introspectionPath, 'utf-8');
734-
introspection = JSON.parse(raw) as IntrospectionTableMeta[];
735-
console.log(`Loaded ${introspection.length} tables from introspection`);
728+
const metaIdx = args.indexOf('--meta');
729+
let meta: MetaTableInfo[] | undefined;
730+
if (metaIdx !== -1 && args[metaIdx + 1]) {
731+
const metaPath = args[metaIdx + 1];
732+
console.log(`Reading _meta from ${metaPath}`);
733+
const raw = readFileSync(metaPath, 'utf-8');
734+
const parsed = JSON.parse(raw);
735+
// Accept both { tables: [...] } (GQL query result) and raw [...] (array)
736+
meta = (Array.isArray(parsed) ? parsed : parsed?.tables ?? parsed?.data?._meta?.tables) as MetaTableInfo[] | undefined;
737+
if (meta) {
738+
console.log(`Loaded ${meta.length} tables from _meta`);
739+
} else {
740+
console.log('Could not find tables in _meta JSON; using static fallback types');
741+
}
736742
} else {
737-
console.log('No --introspection flag; using static fallback types');
743+
console.log('No --meta flag; using static fallback types');
738744
}
739745

740-
const content = buildProgram(introspection);
746+
const content = buildProgram(meta);
741747
const filename = 'blueprint-types.generated.ts';
742748
const filepath = join(outdir, filename);
743749

0 commit comments

Comments
 (0)