diff --git a/graphile/node-type-registry/package.json b/graphile/node-type-registry/package.json
index 8ad1c54375..d4282e2b90 100644
--- a/graphile/node-type-registry/package.json
+++ b/graphile/node-type-registry/package.json
@@ -26,7 +26,8 @@
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
"test": "jest",
- "test:watch": "jest --watch"
+ "test:watch": "jest --watch",
+ "generate:seed": "ts-node src/codegen/generate-seed.ts"
},
"dependencies": {
"@pgsql/types": "^17.6.2",
diff --git a/graphile/node-type-registry/src/codegen/generate-seed.ts b/graphile/node-type-registry/src/codegen/generate-seed.ts
index 4dbfa58fa4..1a94caa45a 100644
--- a/graphile/node-type-registry/src/codegen/generate-seed.ts
+++ b/graphile/node-type-registry/src/codegen/generate-seed.ts
@@ -5,10 +5,16 @@
* suitable for use as separate pgpm migration files.
*
* Usage:
- * npx ts-node src/codegen/generate-seed.ts [--outdir
] [--single]
+ * npx ts-node src/codegen/generate-seed.ts [--outdir ] [--single] [--pgpm ]
*
* --outdir Directory to write individual SQL files (default: stdout)
* --single Emit a single combined seed.sql instead of per-node files
+ * --pgpm Generate deploy/revert/verify files in pgpm package layout.
+ * is the pgpm package root (e.g. packages/metaschema).
+ * Files are written relative to this root at:
+ * deploy/schemas/metaschema_public/tables/node_type_registry/data/seed.sql
+ * revert/schemas/metaschema_public/tables/node_type_registry/data/seed.sql
+ * verify/schemas/metaschema_public/tables/node_type_registry/data/seed.sql
*
* Examples:
* # Print all INSERT statements to stdout
@@ -19,6 +25,9 @@
*
* # Generate a single combined seed file
* npx ts-node src/codegen/generate-seed.ts --single --outdir ./deploy
+ *
+ * # Generate pgpm deploy/revert/verify files
+ * npx ts-node src/codegen/generate-seed.ts --pgpm ../../constructive-db/packages/metaschema
*/
import { writeFileSync, mkdirSync, existsSync } from 'fs';
@@ -29,6 +38,13 @@ import type { Node } from '@pgsql/types';
import { allNodeTypes } from '../index';
import type { NodeTypeDefinition } from '../types';
+// ---------------------------------------------------------------------------
+// Constants
+// ---------------------------------------------------------------------------
+
+const MIGRATION_PATH =
+ 'schemas/metaschema_public/tables/node_type_registry/data/seed';
+
// ---------------------------------------------------------------------------
// AST helpers
// ---------------------------------------------------------------------------
@@ -119,20 +135,123 @@ function buildInsertStmt(nt: NodeTypeDefinition): Node {
};
}
+// ---------------------------------------------------------------------------
+// pgpm file generators
+// ---------------------------------------------------------------------------
+
+const GENERATED_HEADER = [
+ '-- GENERATED FILE — DO NOT EDIT',
+ '--',
+ '-- Regenerate with:',
+ '-- cd graphile/node-type-registry && pnpm generate:seed --pgpm ../../constructive-db/packages/metaschema',
+ '--',
+ '',
+].join('\n');
+
+async function buildDeploySql(): Promise {
+ const header = [
+ `-- Deploy ${MIGRATION_PATH} to pg`,
+ '',
+ GENERATED_HEADER,
+ '-- requires: schemas/metaschema_public/tables/node_type_registry/table',
+ '',
+ ].join('\n');
+
+ const stmts = allNodeTypes.map(buildInsertStmt);
+ const body = await deparse(stmts);
+
+ return header + body + '\n';
+}
+
+function buildRevertSql(): string {
+ const names = allNodeTypes.map((nt) => ` '${nt.name}'`);
+
+ // Wrap names at ~4 per line for readability
+ const chunks: string[] = [];
+ for (let i = 0; i < names.length; i += 4) {
+ chunks.push(names.slice(i, i + 4).join(', '));
+ }
+
+ return [
+ `-- Revert ${MIGRATION_PATH} from pg`,
+ '',
+ GENERATED_HEADER,
+ 'DELETE FROM metaschema_public.node_type_registry',
+ 'WHERE name IN (',
+ chunks.join(',\n'),
+ ');',
+ '',
+ ].join('\n');
+}
+
+function buildVerifySql(): string {
+ // Pick one representative from each category
+ const categories = new Map();
+ for (const nt of allNodeTypes) {
+ if (!categories.has(nt.category)) {
+ categories.set(nt.category, nt);
+ }
+ }
+
+ const checks = Array.from(categories.values()).map(
+ (nt) =>
+ `SELECT 1 FROM metaschema_public.node_type_registry WHERE name = '${nt.name}';`
+ );
+
+ return [
+ `-- Verify ${MIGRATION_PATH} on pg`,
+ '',
+ GENERATED_HEADER,
+ ...checks,
+ '',
+ ].join('\n');
+}
+
+// ---------------------------------------------------------------------------
+// File writer helper
+// ---------------------------------------------------------------------------
+
+function writeFile(filePath: string, content: string): void {
+ const dir = join(filePath, '..');
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
+ writeFileSync(filePath, content);
+}
+
// ---------------------------------------------------------------------------
// CLI
// ---------------------------------------------------------------------------
-function main() {
+async function main() {
const args = process.argv.slice(2);
const outdirIdx = args.indexOf('--outdir');
const outdir = outdirIdx !== -1 ? args[outdirIdx + 1] : undefined;
const single = args.includes('--single');
+ const pgpmIdx = args.indexOf('--pgpm');
+ const pgpmRoot = pgpmIdx !== -1 ? args[pgpmIdx + 1] : undefined;
+
+ // --pgpm mode: generate deploy/revert/verify in pgpm package layout
+ if (pgpmRoot) {
+ const relPath = 'schemas/metaschema_public/tables/node_type_registry/data/seed.sql';
+
+ const deployPath = join(pgpmRoot, 'deploy', relPath);
+ const revertPath = join(pgpmRoot, 'revert', relPath);
+ const verifyPath = join(pgpmRoot, 'verify', relPath);
+
+ writeFile(deployPath, await buildDeploySql());
+ writeFile(revertPath, buildRevertSql());
+ writeFile(verifyPath, buildVerifySql());
+
+ console.log(`Wrote ${allNodeTypes.length} node types to pgpm layout:`);
+ console.log(` deploy: ${deployPath}`);
+ console.log(` revert: ${revertPath}`);
+ console.log(` verify: ${verifyPath}`);
+ return;
+ }
if (single) {
// Emit all INSERT statements as a single SQL string
const stmts = allNodeTypes.map(buildInsertStmt);
- const sql = deparse(stmts);
+ const sql = await deparse(stmts);
if (outdir) {
if (!existsSync(outdir)) mkdirSync(outdir, { recursive: true });
writeFileSync(join(outdir, 'seed.sql'), sql + '\n');
@@ -144,10 +263,12 @@ function main() {
}
// Emit individual SQL files per node type
- const stmts = allNodeTypes.map((nt) => ({
- nt,
- sql: deparse([buildInsertStmt(nt)]),
- }));
+ const stmts = await Promise.all(
+ allNodeTypes.map(async (nt) => ({
+ nt,
+ sql: await deparse([buildInsertStmt(nt)]),
+ }))
+ );
if (outdir) {
if (!existsSync(outdir)) mkdirSync(outdir, { recursive: true });