From b3a99ee95cb501a582b70612dfc29f3c423fe4dd Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Mon, 27 Jul 2026 05:59:14 +0000 Subject: [PATCH 1/5] feat(fixtures): install logical-name plane packages and drop legacy routing schema overrides - pgpm.json: @constructive-db/{apps,catalog,routing}@3.0.0 + routing-platform@2.0.0; routing-functions removed (resolve_route now ships inside routing) - scoped seed fixtures write logical schemas (routing_public/catalog_public/apps_public) and seed the compiled hostname_bindings/route_bindings rows directly since the published schema+grants modules carry no binding-sync triggers - server-test/playwright tests drop the explicit routingSchema overrides and use the server default routing_public - codegen introspection sources query routing_public.api_schemas --- __fixtures__/seed/scoped/test-data.sql | 54 ++++++++++++------- .../src/core/introspect/source/api-schemas.ts | 26 ++++----- .../src/core/introspect/source/database.ts | 2 +- .../src/core/introspect/source/pgpm-module.ts | 4 +- graphql/codegen/src/types/config.ts | 2 +- .../scoped-server.playwright.test.ts | 9 ++-- graphql/playwright-test/src/server.ts | 2 +- .../seed/simple-seed-storage/test-data.sql | 28 +++++----- .../express-context.integration.test.ts | 11 ++-- .../__tests__/fn-routes.integration.test.ts | 11 ++-- .../scoped-routing.integration.test.ts | 25 +++++---- .../__tests__/server.integration.test.ts | 30 +++++------ .../__tests__/upload.integration.test.ts | 9 ++-- pgpm.json | 8 +-- 14 files changed, 114 insertions(+), 107 deletions(-) diff --git a/__fixtures__/seed/scoped/test-data.sql b/__fixtures__/seed/scoped/test-data.sql index 91bb684b0f..e553746682 100644 --- a/__fixtures__/seed/scoped/test-data.sql +++ b/__fixtures__/seed/scoped/test-data.sql @@ -2,19 +2,18 @@ -- -- Scoped routing plane test data: models the "simple-pets" tenant as rows in -- the published modules --- - @constructive-db/catalog -> constructive_catalog_public --- - @constructive-db/routing -> constructive_routing_public --- - @constructive-db/apps -> constructive_apps_public --- - @constructive-db/routing-functions -> resolve_route() + binding-sync triggers +-- - @constructive-db/catalog -> catalog_public +-- - @constructive-db/routing -> routing_public (incl. resolve_route()) +-- - @constructive-db/apps -> apps_public -- -- The metaschema block below seeds metaschema_public directly so this file can -- be composed standalone (routing.api_schemas.schema_id FKs -- metaschema_public.schema). -- --- `constructive_routing_public.{hostname_bindings,route_bindings}` are the --- compiled indexes read by resolve_route(). They are maintained by the --- binding-sync triggers shipped in @constructive-db/routing-functions, so this --- fixture writes only the source rows (domains/routes) with triggers enabled. +-- `routing_public.{hostname_bindings,route_bindings}` are the +-- compiled indexes read by resolve_route(). The published schema+grants +-- modules ship no binding-sync triggers, so this fixture seeds the compiled +-- binding rows directly alongside the source rows (domains/routes). -- -- NOTE: does NOT include app-level schemas/tables or row data. -- Compose with app-schemas/* seeds for that. @@ -73,7 +72,7 @@ VALUES ( ) ON CONFLICT (id) DO NOTHING; -- ===================================================== --- CATALOG DATA (constructive_catalog_public) +-- CATALOG DATA (catalog_public) -- ===================================================== -- -- `apis.config` carries the api surface projection the server folds into @@ -81,7 +80,7 @@ VALUES ( -- is_public, schemas); name/dbname/role_name/anon_role are merged in by the -- resolver from the columns below. -INSERT INTO constructive_catalog_public.apis +INSERT INTO catalog_public.apis (id, owner_scope, owner_key, is_visible, database_id, name, dbname, role_name, anon_role, config) VALUES ( @@ -137,7 +136,7 @@ VALUES ON CONFLICT (id) DO NOTHING; -- Catalog domains: globally-claimed hostnames (same ids as services domains) -INSERT INTO constructive_catalog_public.domains +INSERT INTO catalog_public.domains (id, owner_scope, owner_key, is_visible, database_id, hostname, is_wildcard, parent_hostname, managed, verification_status, tls_status) VALUES ( @@ -151,11 +150,11 @@ VALUES ON CONFLICT (id) DO NOTHING; -- ===================================================== --- ROUTING DATA (constructive_routing_public) +-- ROUTING DATA (routing_public) -- ===================================================== -- Routing-side api surfaces (own the api_schemas / settings joins) -INSERT INTO constructive_routing_public.apis +INSERT INTO routing_public.apis (id, database_id, name, dbname, role_name, anon_role, is_published) VALUES ('6c9997a4-591b-4cb3-9313-4ef45d6f134e', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9', 'app', current_database(), 'authenticated', 'anonymous', true), @@ -166,7 +165,7 @@ VALUES ON CONFLICT (id) DO NOTHING; -- API -> schema linkage -INSERT INTO constructive_routing_public.api_schemas (id, database_id, schema_id, api_id) +INSERT INTO routing_public.api_schemas (id, database_id, schema_id, api_id) VALUES -- app API schemas (public + pets_public) ('71181146-890e-4991-9da7-3dddf87d9e01', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9', '6dbae92a-5450-401b-1ed5-d69e7754940d', '6c9997a4-591b-4cb3-9313-4ef45d6f134e'), @@ -182,7 +181,7 @@ ON CONFLICT (id) DO NOTHING; -- @constructive-db/routing-functions compile hostname_bindings/route_bindings. SET session_replication_role TO DEFAULT; -INSERT INTO constructive_routing_public.domains +INSERT INTO routing_public.domains (id, database_id, hostname, managed, is_wildcard, parent_hostname, verification_status, tls_status, tls_secret_name, is_published) VALUES ('41181146-890e-4991-9da7-3dddf87d9e78', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9', 'app.test.constructive.io', false, false, NULL, 'verified', 'ready', NULL, true), @@ -190,20 +189,37 @@ VALUES ON CONFLICT (id) DO NOTHING; -- Routes: hostname root path -> api surface (source rows) -INSERT INTO constructive_routing_public.routes +INSERT INTO routing_public.routes (id, database_id, domain_id, target_api_id, path, method, priority, is_active) VALUES ('91181146-890e-4991-9da7-3dddf87d9e01', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9', '41181146-890e-4991-9da7-3dddf87d9e78', '6c9997a4-591b-4cb3-9313-4ef45d6f134e', '/', NULL, 0, true), ('91181146-890e-4991-9da7-3dddf87d9e02', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9', '51181146-890e-4991-9da7-3dddf87d9e79', 'e257c53d-6ba6-40de-b679-61b37188a316', '/', NULL, 0, true) ON CONFLICT (id) DO NOTHING; +-- Compiled binding indexes read by resolve_route(). The published routing +-- module ships no binding-sync triggers, so seed the compiled rows directly +-- (hostname_bindings.id mirrors the trigger derivation md5(domain_id|hostname)). +INSERT INTO routing_public.hostname_bindings + (id, hostname, domain_id, is_wildcard, parent_hostname, managed, verification_status, tls_status, tls_secret_name, updated_at) +VALUES + ((md5(concat('41181146-890e-4991-9da7-3dddf87d9e78', '|', 'app.test.constructive.io')))::uuid, 'app.test.constructive.io', '41181146-890e-4991-9da7-3dddf87d9e78', false, NULL, false, 'verified', 'ready', NULL, now()), + ((md5(concat('51181146-890e-4991-9da7-3dddf87d9e79', '|', 'private.test.constructive.io')))::uuid, 'private.test.constructive.io', '51181146-890e-4991-9da7-3dddf87d9e79', false, NULL, false, 'verified', 'ready', NULL, now()) +ON CONFLICT (hostname) DO NOTHING; + +INSERT INTO routing_public.route_bindings + (id, domain_id, target_api_id, target_site_id, target_function_id, path, method, priority, is_active, updated_at) +VALUES + ('91181146-890e-4991-9da7-3dddf87d9e01', '41181146-890e-4991-9da7-3dddf87d9e78', '6c9997a4-591b-4cb3-9313-4ef45d6f134e', NULL, NULL, '/', NULL, 0, true, now()), + ('91181146-890e-4991-9da7-3dddf87d9e02', '51181146-890e-4991-9da7-3dddf87d9e79', 'e257c53d-6ba6-40de-b679-61b37188a316', NULL, NULL, '/', NULL, 0, true, now()) +ON CONFLICT (id) DO NOTHING; + SET session_replication_role TO replica; -- ===================================================== --- APPS DATA (constructive_apps_public) +-- APPS DATA (apps_public) -- ===================================================== -INSERT INTO constructive_apps_public.apps +INSERT INTO apps_public.apps (id, database_id, name, title, description, status, is_published) VALUES ( 'c1181146-890e-4991-9da7-3dddf87d9e01', @@ -215,7 +231,7 @@ VALUES ( true ) ON CONFLICT (id) DO NOTHING; -INSERT INTO constructive_apps_public.app_components +INSERT INTO apps_public.app_components (id, database_id, app_id, component_api_id, component_domain_id, component_type) VALUES ('d1181146-890e-4991-9da7-3dddf87d9e01', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9', 'c1181146-890e-4991-9da7-3dddf87d9e01', '6c9997a4-591b-4cb3-9313-4ef45d6f134e', NULL, 'api'), diff --git a/graphql/codegen/src/core/introspect/source/api-schemas.ts b/graphql/codegen/src/core/introspect/source/api-schemas.ts index a1b2f871e6..f73c42d19a 100644 --- a/graphql/codegen/src/core/introspect/source/api-schemas.ts +++ b/graphql/codegen/src/core/introspect/source/api-schemas.ts @@ -2,7 +2,7 @@ * API Schemas Resolution * * Utilities for resolving PostgreSQL schema names from API names - * by querying the constructive_routing_public.api_schemas table. + * by querying the routing_public.api_schemas table. */ import { Pool } from 'pg'; import { getPgPool } from 'pg-cache'; @@ -20,7 +20,7 @@ export interface RoutingSchemaValidation { * Validate that the required routing schemas exist in the database * * Checks for: - * - constructive_routing_public schema with apis and api_schemas tables + * - routing_public schema with apis and api_schemas tables * - metaschema_public schema with schema table * * @param pool - Database connection pool @@ -30,31 +30,31 @@ export async function validateRoutingSchemas( pool: Pool, ): Promise { try { - // Check for constructive_routing_public.apis table + // Check for routing_public.apis table const apisCheck = await pool.query(` SELECT 1 FROM information_schema.tables - WHERE table_schema = 'constructive_routing_public' + WHERE table_schema = 'routing_public' AND table_name = 'apis' `); if (apisCheck.rows.length === 0) { return { valid: false, error: - 'constructive_routing_public.apis table not found. The database must have the routing schema deployed.', + 'routing_public.apis table not found. The database must have the routing schema deployed.', }; } - // Check for constructive_routing_public.api_schemas table + // Check for routing_public.api_schemas table const apiSchemasCheck = await pool.query(` SELECT 1 FROM information_schema.tables - WHERE table_schema = 'constructive_routing_public' + WHERE table_schema = 'routing_public' AND table_name = 'api_schemas' `); if (apiSchemasCheck.rows.length === 0) { return { valid: false, error: - 'constructive_routing_public.api_schemas table not found. The database must have the routing schema deployed.', + 'routing_public.api_schemas table not found. The database must have the routing schema deployed.', }; } @@ -82,9 +82,9 @@ export async function validateRoutingSchemas( } /** - * Resolve schema names from API names by querying constructive_routing_public.api_schemas + * Resolve schema names from API names by querying routing_public.api_schemas * - * Joins constructive_routing_public.apis, constructive_routing_public.api_schemas, and metaschema_public.schema + * Joins routing_public.apis, routing_public.api_schemas, and metaschema_public.schema * to get the actual PostgreSQL schema names for the given API names. * * @param pool - Database connection pool @@ -106,8 +106,8 @@ export async function resolveApiSchemas( const result = await pool.query<{ schema_name: string }>( ` SELECT DISTINCT ms.schema_name - FROM constructive_routing_public.api_schemas as_tbl - JOIN constructive_routing_public.apis api ON api.id = as_tbl.api_id + FROM routing_public.api_schemas as_tbl + JOIN routing_public.apis api ON api.id = as_tbl.api_id JOIN metaschema_public.schema ms ON ms.id = as_tbl.schema_id WHERE api.name = ANY($1) ORDER BY ms.schema_name @@ -118,7 +118,7 @@ export async function resolveApiSchemas( if (result.rows.length === 0) { throw new Error( `No schemas found for API names: ${apiNames.join(', ')}. ` + - 'Ensure the APIs exist and have schemas assigned in constructive_routing_public.api_schemas.', + 'Ensure the APIs exist and have schemas assigned in routing_public.api_schemas.', ); } diff --git a/graphql/codegen/src/core/introspect/source/database.ts b/graphql/codegen/src/core/introspect/source/database.ts index 82fa8e9592..69e2f15cee 100644 --- a/graphql/codegen/src/core/introspect/source/database.ts +++ b/graphql/codegen/src/core/introspect/source/database.ts @@ -34,7 +34,7 @@ export interface DatabaseSchemaSourceOptions { /** * API names to resolve schemas from - * Queries constructive_routing_public.api_schemas to get schema names + * Queries routing_public.api_schemas to get schema names * Mutually exclusive with schemas */ apiNames?: string[]; diff --git a/graphql/codegen/src/core/introspect/source/pgpm-module.ts b/graphql/codegen/src/core/introspect/source/pgpm-module.ts index 37824d41d3..9d25fe782d 100644 --- a/graphql/codegen/src/core/introspect/source/pgpm-module.ts +++ b/graphql/codegen/src/core/introspect/source/pgpm-module.ts @@ -38,7 +38,7 @@ export interface PgpmModulePathOptions { /** * API names to resolve schemas from - * Queries constructive_routing_public.api_schemas to get schema names + * Queries routing_public.api_schemas to get schema names * Mutually exclusive with schemas */ apiNames?: string[]; @@ -73,7 +73,7 @@ export interface PgpmWorkspaceOptions { /** * API names to resolve schemas from - * Queries constructive_routing_public.api_schemas to get schema names + * Queries routing_public.api_schemas to get schema names * Mutually exclusive with schemas */ apiNames?: string[]; diff --git a/graphql/codegen/src/types/config.ts b/graphql/codegen/src/types/config.ts index f9ebf37d73..14261fb1bb 100644 --- a/graphql/codegen/src/types/config.ts +++ b/graphql/codegen/src/types/config.ts @@ -122,7 +122,7 @@ export interface DbConfig { /** * API names to resolve schemas from - * Queries constructive_routing_public.api_schemas to automatically determine schemas + * Queries routing_public.api_schemas to automatically determine schemas * Mutually exclusive with `schemas` * @example ['my_api'] */ diff --git a/graphql/playwright-test/__tests__/scoped-server.playwright.test.ts b/graphql/playwright-test/__tests__/scoped-server.playwright.test.ts index 9c97b26cbe..cbf9364ba3 100644 --- a/graphql/playwright-test/__tests__/scoped-server.playwright.test.ts +++ b/graphql/playwright-test/__tests__/scoped-server.playwright.test.ts @@ -4,7 +4,7 @@ * Proves the Playwright harness can drive the *production* * `@constructive-io/graphql-server` (not the dev server): every request is * resolved through the scoped-routing plane - * (`constructive_routing_public.resolve_route()`), so this suite seeds real + * (`routing_public.resolve_route()`), so this suite seeds real * routing/database records and reaches the api surface via its seeded host. * * Contrast with `server.playwright.test.ts`, which uses the single-tenant dev @@ -21,9 +21,9 @@ const pgpmWorkspace = path.join(sharedSeedRoot, '..', '..'); const schemas = ['simple-pets-public', 'simple-pets-pets-public']; const scopedMetaSchemas = [ - 'constructive_catalog_public', - 'constructive_routing_public', - 'constructive_apps_public', + 'catalog_public', + 'routing_public', + 'apps_public', 'metaschema_public', 'metaschema_modules_public' ]; @@ -48,7 +48,6 @@ test.describe('playwright-test scoped server (real graphql-server)', () => { server: { useRouting: true, api: { - routingSchema: 'constructive_routing_public', isPublic: true, metaSchemas: scopedMetaSchemas } diff --git a/graphql/playwright-test/src/server.ts b/graphql/playwright-test/src/server.ts index e3479b3ee7..53e2627a16 100644 --- a/graphql/playwright-test/src/server.ts +++ b/graphql/playwright-test/src/server.ts @@ -34,7 +34,7 @@ export const createTestServer = async ( * * Uses the `Server` class from `@constructive-io/graphql-server` directly, so * every request is resolved through the scoped-routing plane - * (`constructive_routing_public.resolve_route()`). Suites using this must seed + * (`routing_public.resolve_route()`). Suites using this must seed * real routing/database records so a real database id is resolved. */ export const createScopedTestServer = async ( diff --git a/graphql/server-test/__fixtures__/seed/simple-seed-storage/test-data.sql b/graphql/server-test/__fixtures__/seed/simple-seed-storage/test-data.sql index 17d8d9a57b..b427abed27 100644 --- a/graphql/server-test/__fixtures__/seed/simple-seed-storage/test-data.sql +++ b/graphql/server-test/__fixtures__/seed/simple-seed-storage/test-data.sql @@ -44,15 +44,15 @@ VALUES ( ) ON CONFLICT (id) DO NOTHING; -- ===================================================== --- ROUTING DATA (constructive_routing_public) +-- ROUTING DATA (routing_public) -- ===================================================== -INSERT INTO constructive_routing_public.apis (id, database_id, name, dbname, is_published, role_name, anon_role) +INSERT INTO routing_public.apis (id, database_id, name, dbname, is_published, role_name, anon_role) VALUES ('6c9997a4-591b-4cb3-9313-4ef45d6f134e', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9', 'app', current_database(), false, 'authenticated', 'anonymous') ON CONFLICT (id) DO NOTHING; -INSERT INTO constructive_routing_public.api_schemas (id, database_id, schema_id, api_id) +INSERT INTO routing_public.api_schemas (id, database_id, schema_id, api_id) VALUES ('71181146-890e-4991-9da7-3dddf87d9e01', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9', '6dbae92a-5450-401b-1ed5-d69e7754940d', '6c9997a4-591b-4cb3-9313-4ef45d6f134e') ON CONFLICT (id) DO NOTHING; @@ -100,7 +100,7 @@ ON CONFLICT (id) DO NOTHING; -- ALICE DATABASE SETTINGS (all defaults — presigned uploads enabled) -- ===================================================== -INSERT INTO constructive_routing_public.database_settings (id, database_id) +INSERT INTO routing_public.database_settings (id, database_id) VALUES ( 'e0000001-0000-0000-0000-000000000001', '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9' @@ -130,22 +130,22 @@ VALUES ON CONFLICT (id) DO NOTHING; -- ===================================================== --- BOB ROUTING DATA (constructive_routing_public) +-- BOB ROUTING DATA (routing_public) -- ===================================================== -- Bob's primary API (presigned uploads enabled via database_settings defaults) -INSERT INTO constructive_routing_public.apis (id, database_id, name, dbname, is_published, role_name, anon_role) +INSERT INTO routing_public.apis (id, database_id, name, dbname, is_published, role_name, anon_role) VALUES ('a3a3a3a3-b4b4-4c5c-d6d6-e7e7e7e7e7e7', 'a1a1a1a1-b2b2-4c3c-d4d4-e5e5e5e5e5e5', 'bob-app', current_database(), false, 'authenticated', 'anonymous') ON CONFLICT (id) DO NOTHING; -- Bob's restricted API (api_settings will disable presigned uploads) -INSERT INTO constructive_routing_public.apis (id, database_id, name, dbname, is_published, role_name, anon_role) +INSERT INTO routing_public.apis (id, database_id, name, dbname, is_published, role_name, anon_role) VALUES ('a4a4a4a4-b5b5-4c6c-d7d7-e8e8e8e8e8e8', 'a1a1a1a1-b2b2-4c3c-d4d4-e5e5e5e5e5e5', 'bob-restricted', current_database(), false, 'authenticated', 'anonymous') ON CONFLICT (id) DO NOTHING; -INSERT INTO constructive_routing_public.api_schemas (id, database_id, schema_id, api_id) +INSERT INTO routing_public.api_schemas (id, database_id, schema_id, api_id) VALUES ('a5a5a5a5-0000-0000-0000-000000000001', 'a1a1a1a1-b2b2-4c3c-d4d4-e5e5e5e5e5e5', 'a2a2a2a2-b3b3-4c4c-d5d5-e6e6e6e6e6e6', 'a3a3a3a3-b4b4-4c5c-d6d6-e7e7e7e7e7e7'), ('a5a5a5a5-0000-0000-0000-000000000002', 'a1a1a1a1-b2b2-4c3c-d4d4-e5e5e5e5e5e5', 'a2a2a2a2-b3b3-4c4c-d5d5-e6e6e6e6e6e6', 'a4a4a4a4-b5b5-4c6c-d7d7-e8e8e8e8e8e8') @@ -220,7 +220,7 @@ VALUES ( -- BOB DATABASE SETTINGS (all defaults — presigned uploads enabled) -- ===================================================== -INSERT INTO constructive_routing_public.database_settings (id, database_id) +INSERT INTO routing_public.database_settings (id, database_id) VALUES ( 'e0000001-0000-0000-0000-000000000002', 'a1a1a1a1-b2b2-4c3c-d4d4-e5e5e5e5e5e5' @@ -230,7 +230,7 @@ VALUES ( -- BOB API SETTINGS (restricted API disables presigned uploads) -- ===================================================== -INSERT INTO constructive_routing_public.api_settings (id, database_id, api_id, enable_presigned_uploads) +INSERT INTO routing_public.api_settings (id, database_id, api_id, enable_presigned_uploads) VALUES ( 'f0000001-0000-0000-0000-000000000001', 'a1a1a1a1-b2b2-4c3c-d4d4-e5e5e5e5e5e5', @@ -262,15 +262,15 @@ VALUES ON CONFLICT (id) DO NOTHING; -- ===================================================== --- MALLORY ROUTING DATA (constructive_routing_public) +-- MALLORY ROUTING DATA (routing_public) -- ===================================================== -INSERT INTO constructive_routing_public.apis (id, database_id, name, dbname, is_published, role_name, anon_role) +INSERT INTO routing_public.apis (id, database_id, name, dbname, is_published, role_name, anon_role) VALUES ('fa44fa44-a5a5-4b6b-c7c7-d8d8d8d8d8d8', 'fa11fa11-a2a2-4b3b-c4c4-d5d5d5d5d5d5', 'mallory-app', current_database(), false, 'authenticated', 'anonymous') ON CONFLICT (id) DO NOTHING; -INSERT INTO constructive_routing_public.api_schemas (id, database_id, schema_id, api_id) +INSERT INTO routing_public.api_schemas (id, database_id, schema_id, api_id) VALUES ('fa55fa55-0000-0000-0000-000000000001', 'fa11fa11-a2a2-4b3b-c4c4-d5d5d5d5d5d5', 'fa22fa22-a3a3-4b4b-c5c5-d6d6d6d6d6d6', 'fa44fa44-a5a5-4b6b-c7c7-d8d8d8d8d8d8') ON CONFLICT (id) DO NOTHING; @@ -343,7 +343,7 @@ VALUES ( -- MALLORY DATABASE SETTINGS (all defaults — presigned uploads enabled) -- ===================================================== -INSERT INTO constructive_routing_public.database_settings (id, database_id) +INSERT INTO routing_public.database_settings (id, database_id) VALUES ( 'fa88fa88-0000-0000-0000-000000000001', 'fa11fa11-a2a2-4b3b-c4c4-d5d5d5d5d5d5' diff --git a/graphql/server-test/__tests__/express-context.integration.test.ts b/graphql/server-test/__tests__/express-context.integration.test.ts index b62cc6eb97..ca0979086a 100644 --- a/graphql/server-test/__tests__/express-context.integration.test.ts +++ b/graphql/server-test/__tests__/express-context.integration.test.ts @@ -9,7 +9,7 @@ * * Uses the shared seed fixtures from __fixtures__/seed/ (repo root-level) * for the full metaschema + scoped routing plane + app-schemas stack. - * Host routing resolves through constructive_routing_public.resolve_route. + * Host routing resolves through routing_public.resolve_route. * * Run tests: * pnpm test -- --testPathPattern=express-context.integration @@ -29,9 +29,9 @@ const shared = (...segments: string[]) => const pgpmWorkspace = path.join(sharedSeedRoot, '..', '..'); const schemas = ['simple-pets-public', 'simple-pets-pets-public']; const metaSchemas = [ - 'constructive_catalog_public', - 'constructive_routing_public', - 'constructive_apps_public', + 'catalog_public', + 'routing_public', + 'apps_public', 'metaschema_public', 'metaschema_modules_public' ]; @@ -58,8 +58,7 @@ beforeAll(async () => { useRouting: true, api: { isPublic: true, - metaSchemas, - routingSchema: 'constructive_routing_public' + metaSchemas } } }, diff --git a/graphql/server-test/__tests__/fn-routes.integration.test.ts b/graphql/server-test/__tests__/fn-routes.integration.test.ts index 95ef212c44..e9b77de9e0 100644 --- a/graphql/server-test/__tests__/fn-routes.integration.test.ts +++ b/graphql/server-test/__tests__/fn-routes.integration.test.ts @@ -9,7 +9,7 @@ * * The seeded function_invocations INSERT policy only passes when * `jwt.claims.api_id` (set transaction-locally by the server from the - * hostname → constructive_routing_public.resolve_route → api_id resolution) + * hostname → routing_public.resolve_route → api_id resolution) * matches the binding's api_id — so the happy path also proves the claim is * injected. * @@ -30,9 +30,9 @@ const shared = (...segments: string[]) => const pgpmWorkspace = path.join(sharedSeedRoot, '..', '..'); const schemas = ['simple-pets-public', 'simple-pets-pets-public']; const metaSchemas = [ - 'constructive_catalog_public', - 'constructive_routing_public', - 'constructive_apps_public', + 'catalog_public', + 'routing_public', + 'apps_public', 'metaschema_public', 'metaschema_modules_public' ]; @@ -62,8 +62,7 @@ beforeAll(async () => { useRouting: true, api: { isPublic: true, - metaSchemas, - routingSchema: 'constructive_routing_public' + metaSchemas } } }, diff --git a/graphql/server-test/__tests__/scoped-routing.integration.test.ts b/graphql/server-test/__tests__/scoped-routing.integration.test.ts index ded1d7fe34..90222f9c57 100644 --- a/graphql/server-test/__tests__/scoped-routing.integration.test.ts +++ b/graphql/server-test/__tests__/scoped-routing.integration.test.ts @@ -2,9 +2,9 @@ * Scoped Routing Integration Tests (simple-seed-scoped) * * Exercises the new schema-only pgpm modules - * @constructive-db/catalog → constructive_catalog_public - * @constructive-db/routing → constructive_routing_public - * @constructive-db/apps → constructive_apps_public + * @constructive-db/catalog → catalog_public + * @constructive-db/routing → routing_public + * @constructive-db/apps → apps_public * seeded from `__fixtures__/seed/scoped/*` for the same "simple-pets" tenant * that the retired legacy fixture used to model. * @@ -29,14 +29,14 @@ const scopedDatabaseId = '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9'; const appApiId = '6c9997a4-591b-4cb3-9313-4ef45d6f134e'; const privateApiId = 'e257c53d-6ba6-40de-b679-61b37188a316'; const scopedMetaSchemas = [ - 'constructive_catalog_public', - 'constructive_routing_public', - 'constructive_apps_public', + 'catalog_public', + 'routing_public', + 'apps_public', 'metaschema_public', 'metaschema_modules_public' ]; -/** Row shape returned by constructive_routing_public.resolve_route(). */ +/** Row shape returned by routing_public.resolve_route(). */ interface ResolvedRouteRow { route_binding_id: string | null; hostname: string | null; @@ -66,7 +66,7 @@ describe('simple-seed-scoped: resolve_route (SQL level)', () => { const resolveRoute = (host: string) => pg.oneOrNone( - `SELECT * FROM constructive_routing_public.resolve_route($1, '/', NULL)`, + `SELECT * FROM routing_public.resolve_route($1, '/', NULL)`, [host] ); @@ -90,10 +90,10 @@ describe('simple-seed-scoped: resolve_route (SQL level)', () => { `SELECT nspname FROM pg_namespace WHERE nspname LIKE 'constructive_%' ORDER BY nspname` ); expect(rows.map((r) => r.nspname)).toEqual([ - 'constructive_apps_public', - 'constructive_catalog_public', + 'apps_public', + 'catalog_public', 'constructive_routing_private', - 'constructive_routing_public' + 'routing_public' ]); }); @@ -155,7 +155,7 @@ describe('simple-seed-scoped: resolve_route (SQL level)', () => { /** * End-to-end scoped plane: the request is resolved solely by - * constructive_routing_public.resolve_route(). There is + * routing_public.resolve_route(). There is * no legacy fallback, so a successful request proves the scoped path. */ describe('simple-seed-scoped: GraphQL over scoped routing (e2e)', () => { @@ -176,7 +176,6 @@ describe('simple-seed-scoped: GraphQL over scoped routing (e2e)', () => { server: { useRouting: true, api: { - routingSchema: 'constructive_routing_public', isPublic: true, metaSchemas: scopedMetaSchemas } diff --git a/graphql/server-test/__tests__/server.integration.test.ts b/graphql/server-test/__tests__/server.integration.test.ts index 5441b52d82..cec379129a 100644 --- a/graphql/server-test/__tests__/server.integration.test.ts +++ b/graphql/server-test/__tests__/server.integration.test.ts @@ -2,7 +2,7 @@ * Server Integration Tests using graphql-server-test * * All host routing resolves through the scoped routing plane - * (constructive_routing_public.resolve_route); there is no legacy fallback. + * (routing_public.resolve_route); there is no legacy fallback. * The single-tenant scenario runs against the dev server * (server.useRouting: false), which exposes the configured schemas * directly with no route resolution. @@ -26,9 +26,9 @@ const pgpmWorkspace = path.join(sharedSeedRoot, '..', '..'); const schemas = ['simple-pets-public', 'simple-pets-pets-public']; const scopedDatabaseId = '80a2eaaf-f77e-4bfe-8506-df929ef1b8d9'; const scopedMetaSchemas = [ - 'constructive_catalog_public', - 'constructive_routing_public', - 'constructive_apps_public', + 'catalog_public', + 'routing_public', + 'apps_public', 'metaschema_public', 'metaschema_modules_public' ]; @@ -37,7 +37,7 @@ const scopedMetaSchemas = [ // cannot be exposed together in one PostGraphile schema. Expose the // authoritative routing plane plus the metaschema tables. const metaApiSchemas = [ - 'constructive_routing_public', + 'routing_public', 'metaschema_public', 'metaschema_modules_public' ]; @@ -66,7 +66,7 @@ const scenarios: Scenario[] = [ name: 'scoped public via domain', seedDir: 'simple-seed-scoped', useRouting: true, - api: { isPublic: true, metaSchemas: scopedMetaSchemas, routingSchema: 'constructive_routing_public' }, + api: { isPublic: true, metaSchemas: scopedMetaSchemas }, headers: { Host: 'app.test.constructive.io' } @@ -75,7 +75,7 @@ const scenarios: Scenario[] = [ name: 'scoped private via domain', seedDir: 'simple-seed-scoped', useRouting: true, - api: { isPublic: false, metaSchemas: scopedMetaSchemas, routingSchema: 'constructive_routing_public' }, + api: { isPublic: false, metaSchemas: scopedMetaSchemas }, headers: { Host: 'private.test.constructive.io' } @@ -84,7 +84,7 @@ const scenarios: Scenario[] = [ name: 'scoped private via X-Api-Name', seedDir: 'simple-seed-scoped', useRouting: true, - api: { isPublic: false, metaSchemas: scopedMetaSchemas, routingSchema: 'constructive_routing_public' }, + api: { isPublic: false, metaSchemas: scopedMetaSchemas }, headers: { 'X-Database-Id': scopedDatabaseId, 'X-Api-Name': 'private' @@ -94,7 +94,7 @@ const scenarios: Scenario[] = [ name: 'scoped private via X-Schemata', seedDir: 'simple-seed-scoped', useRouting: true, - api: { isPublic: false, metaSchemas: scopedMetaSchemas, routingSchema: 'constructive_routing_public' }, + api: { isPublic: false, metaSchemas: scopedMetaSchemas }, headers: { 'X-Database-Id': scopedDatabaseId, 'X-Schemata': schemas.join(',') @@ -284,8 +284,7 @@ describe('scoped private via X-Meta-Schema', () => { useRouting: true, api: { isPublic: false, - metaSchemas: metaApiSchemas, - routingSchema: 'constructive_routing_public' + metaSchemas: metaApiSchemas } } }, @@ -360,8 +359,7 @@ describe('Error paths', () => { useRouting: true, api: { isPublic: false, - metaSchemas: scopedMetaSchemas, - routingSchema: 'constructive_routing_public' + metaSchemas: scopedMetaSchemas } } }, @@ -410,8 +408,7 @@ describe('Error paths', () => { useRouting: true, api: { isPublic: false, - metaSchemas: scopedMetaSchemas, - routingSchema: 'constructive_routing_public' + metaSchemas: scopedMetaSchemas } } }, @@ -445,8 +442,7 @@ describe('Error paths', () => { useRouting: true, api: { isPublic: true, - metaSchemas: scopedMetaSchemas, - routingSchema: 'constructive_routing_public' + metaSchemas: scopedMetaSchemas } } }, diff --git a/graphql/server-test/__tests__/upload.integration.test.ts b/graphql/server-test/__tests__/upload.integration.test.ts index bae2f7e77e..e716edcfd5 100644 --- a/graphql/server-test/__tests__/upload.integration.test.ts +++ b/graphql/server-test/__tests__/upload.integration.test.ts @@ -64,9 +64,9 @@ const malloryPublicFileId = 'fa99fa99-0000-0000-0000-000000000001'; const malloryPublicBucketId = 'fa77fa77-0000-0000-0000-000000000001'; const metaSchemas = [ - 'constructive_catalog_public', - 'constructive_routing_public', - 'constructive_apps_public', + 'catalog_public', + 'routing_public', + 'apps_public', 'metaschema_public', 'metaschema_modules_public' ]; @@ -318,8 +318,7 @@ describe('Integration tests (uploads, tenant isolation, RLS)', () => { useRouting: true, api: { isPublic: false, - metaSchemas, - routingSchema: 'constructive_routing_public' + metaSchemas } } }, diff --git a/pgpm.json b/pgpm.json index d5e14b87ee..316d60e2ee 100644 --- a/pgpm.json +++ b/pgpm.json @@ -3,10 +3,10 @@ "testing/*" ], "dependencies": { - "@constructive-db/apps": "1.0.1", - "@constructive-db/catalog": "1.0.1", - "@constructive-db/routing": "1.0.1", - "@constructive-db/routing-functions": "1.1.0", + "@constructive-db/apps": "3.0.0", + "@constructive-db/catalog": "3.0.0", + "@constructive-db/routing": "3.0.0", + "@constructive-db/routing-platform": "3.0.0", "@pgpm/database-jobs": "0.33.1", "@pgpm/function-resolution": "0.33.1", "@pgpm/jwt-claims": "0.33.0", From f2e640c4bd3d3f0f50f2413e339ee0a975b6dcac Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Mon, 27 Jul 2026 08:34:21 +0000 Subject: [PATCH 2/5] fix(fixtures): pin schema+grants-only plane packages (routing@4, catalog/apps@3.1, routing-platform@3) The published surface now carries only the routing read-path resolvers and the catalog-sync propagation triggers; binding-sync stays unpublished so the compiled hostname_bindings/route_bindings rows remain seeded directly. Schema assertion updated for the logical-name plane schemas. --- __fixtures__/seed/scoped/test-data.sql | 7 +++++-- .../__tests__/scoped-routing.integration.test.ts | 6 ++++-- pgpm.json | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/__fixtures__/seed/scoped/test-data.sql b/__fixtures__/seed/scoped/test-data.sql index e553746682..dce0a856b4 100644 --- a/__fixtures__/seed/scoped/test-data.sql +++ b/__fixtures__/seed/scoped/test-data.sql @@ -177,8 +177,11 @@ VALUES ON CONFLICT (id) DO NOTHING; -- Routing-side domains (source rows for the compiled hostname index). --- Triggers are re-enabled so the binding-sync triggers from --- @constructive-db/routing-functions compile hostname_bindings/route_bindings. +-- Triggers are re-enabled here: the published modules attach only the +-- catalog-sync propagation triggers, whose upserts into catalog_public are +-- idempotent with the direct catalog seeds above. Binding-sync triggers are +-- NOT published, so the compiled hostname_bindings/route_bindings rows are +-- still seeded directly below. SET session_replication_role TO DEFAULT; INSERT INTO routing_public.domains diff --git a/graphql/server-test/__tests__/scoped-routing.integration.test.ts b/graphql/server-test/__tests__/scoped-routing.integration.test.ts index 90222f9c57..589237e354 100644 --- a/graphql/server-test/__tests__/scoped-routing.integration.test.ts +++ b/graphql/server-test/__tests__/scoped-routing.integration.test.ts @@ -87,12 +87,14 @@ describe('simple-seed-scoped: resolve_route (SQL level)', () => { it('deploys the scoped catalog/routing/apps schemas', async () => { const rows = await pg.any<{ nspname: string }>( - `SELECT nspname FROM pg_namespace WHERE nspname LIKE 'constructive_%' ORDER BY nspname` + `SELECT nspname FROM pg_namespace + WHERE nspname IN ('apps_public', 'catalog_public', 'routing_public', 'routing_private') + ORDER BY nspname` ); expect(rows.map((r) => r.nspname)).toEqual([ 'apps_public', 'catalog_public', - 'constructive_routing_private', + 'routing_private', 'routing_public' ]); }); diff --git a/pgpm.json b/pgpm.json index 316d60e2ee..961cb378e1 100644 --- a/pgpm.json +++ b/pgpm.json @@ -3,9 +3,9 @@ "testing/*" ], "dependencies": { - "@constructive-db/apps": "3.0.0", - "@constructive-db/catalog": "3.0.0", - "@constructive-db/routing": "3.0.0", + "@constructive-db/apps": "3.1.0", + "@constructive-db/catalog": "3.1.0", + "@constructive-db/routing": "4.0.0", "@constructive-db/routing-platform": "3.0.0", "@pgpm/database-jobs": "0.33.1", "@pgpm/function-resolution": "0.33.1", From f36e99504b3dbe1d6cae94a67370c35467691709 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Mon, 27 Jul 2026 09:06:18 +0000 Subject: [PATCH 3/5] fix(fixtures): pin the actually-published plane package versions (routing@5.0.0, catalog/apps/routing-platform@4.0.0) --- pgpm.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pgpm.json b/pgpm.json index 961cb378e1..83883b1613 100644 --- a/pgpm.json +++ b/pgpm.json @@ -3,10 +3,10 @@ "testing/*" ], "dependencies": { - "@constructive-db/apps": "3.1.0", - "@constructive-db/catalog": "3.1.0", + "@constructive-db/apps": "4.0.0", + "@constructive-db/catalog": "4.0.0", "@constructive-db/routing": "4.0.0", - "@constructive-db/routing-platform": "3.0.0", + "@constructive-db/routing-platform": "4.0.0", "@pgpm/database-jobs": "0.33.1", "@pgpm/function-resolution": "0.33.1", "@pgpm/jwt-claims": "0.33.0", From 69b85f8ed17b2d182d35e5e0f981bd2fb83c264e Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Mon, 27 Jul 2026 09:14:00 +0000 Subject: [PATCH 4/5] fix(fixtures): pin routing@5.0.0 (pgpm install -W had rewritten it to 4.0.0) --- pgpm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpm.json b/pgpm.json index 83883b1613..15097cfa84 100644 --- a/pgpm.json +++ b/pgpm.json @@ -5,7 +5,7 @@ "dependencies": { "@constructive-db/apps": "4.0.0", "@constructive-db/catalog": "4.0.0", - "@constructive-db/routing": "4.0.0", + "@constructive-db/routing": "5.0.0", "@constructive-db/routing-platform": "4.0.0", "@pgpm/database-jobs": "0.33.1", "@pgpm/function-resolution": "0.33.1", From 5f62913c1ee35d70d587157ec7c4edd7c15ec72f Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Mon, 27 Jul 2026 09:22:16 +0000 Subject: [PATCH 5/5] ci(examples): run examples-types on Node 22 (graphile-settings requires >= 22) --- .github/workflows/examples-integration.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/examples-integration.yaml b/.github/workflows/examples-integration.yaml index d9191e344e..45d10e3fbe 100644 --- a/.github/workflows/examples-integration.yaml +++ b/.github/workflows/examples-integration.yaml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' - uses: pnpm/action-setup@v2 with: version: 10