From a96d9486943708843b4f11e2110550a68eaac593 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Thu, 5 Feb 2026 04:56:49 +0000 Subject: [PATCH 1/5] feat: inline pg-type-mappings plugin into graphile-settings - Add PgTypeMappingsPlugin that maps custom PostgreSQL types to GraphQL scalars - Default mappings: email, hostname, url, origin -> String; multiple_select, single_select -> JSON - Uses v5 gather hooks to create codecs for custom types - Uses v5 schema hooks to map codecs to GraphQL types - Add PgTypeMappingsPreset to ConstructivePreset - Export plugin and types from plugins/index.ts --- .../graphile-settings/src/plugins/index.ts | 7 + .../src/plugins/pg-type-mappings.ts | 162 ++++++++++++++++++ .../src/presets/constructive-preset.ts | 3 + 3 files changed, 172 insertions(+) create mode 100644 graphile/graphile-settings/src/plugins/pg-type-mappings.ts diff --git a/graphile/graphile-settings/src/plugins/index.ts b/graphile/graphile-settings/src/plugins/index.ts index b62915013..77f115423 100644 --- a/graphile/graphile-settings/src/plugins/index.ts +++ b/graphile/graphile-settings/src/plugins/index.ts @@ -61,3 +61,10 @@ export { TsvectorCodecPlugin, TsvectorCodecPreset, } from './tsvector-codec'; + +// PG type mappings for custom PostgreSQL types (email, url, etc.) +export { + PgTypeMappingsPlugin, + PgTypeMappingsPreset, +} from './pg-type-mappings'; +export type { TypeMapping } from './pg-type-mappings'; diff --git a/graphile/graphile-settings/src/plugins/pg-type-mappings.ts b/graphile/graphile-settings/src/plugins/pg-type-mappings.ts new file mode 100644 index 000000000..213c4cd44 --- /dev/null +++ b/graphile/graphile-settings/src/plugins/pg-type-mappings.ts @@ -0,0 +1,162 @@ +import type { GraphileConfig } from 'graphile-config'; +import { GraphQLString } from 'grafast/graphql'; +import sql from 'pg-sql2'; + +/** + * Type mapping configuration for custom PostgreSQL types. + */ +export interface TypeMapping { + /** PostgreSQL type name */ + name: string; + /** PostgreSQL schema/namespace name */ + namespaceName: string; + /** GraphQL type to map to ('String' or 'JSON') */ + type: 'String' | 'JSON'; +} + +/** + * Default type mappings for common custom PostgreSQL types. + * These are typically domain types or composite types that should be + * represented as simple scalars in GraphQL. + */ +const DEFAULT_MAPPINGS: TypeMapping[] = [ + { name: 'email', namespaceName: 'public', type: 'String' }, + { name: 'hostname', namespaceName: 'public', type: 'String' }, + { name: 'multiple_select', namespaceName: 'public', type: 'JSON' }, + { name: 'single_select', namespaceName: 'public', type: 'JSON' }, + { name: 'origin', namespaceName: 'public', type: 'String' }, + { name: 'url', namespaceName: 'public', type: 'String' }, +]; + +/** + * Plugin that maps custom PostgreSQL types to GraphQL scalar types. + * + * This is useful for domain types or composite types that should be + * represented as simple scalars (String, JSON) in the GraphQL API. + * + * For example, if you have: + * CREATE DOMAIN email AS text; + * CREATE TYPE url AS (value text); + * + * This plugin will map them to GraphQL String type instead of creating + * complex object types. + * + * The plugin handles both: + * 1. Domain types (simple aliases) - maps directly to the target scalar + * 2. Composite types - extracts the first field's value when converting from PG + */ +export const PgTypeMappingsPlugin: GraphileConfig.Plugin = { + name: 'PgTypeMappingsPlugin', + version: '1.0.0', + + gather: { + hooks: { + async pgCodecs_findPgCodec(info, event) { + if (event.pgCodec) { + return; + } + + const { pgType: type, serviceName } = event; + + // Find the namespace for this type + const namespace = await info.helpers.pgIntrospection.getNamespace( + serviceName, + type.typnamespace + ); + + if (!namespace) { + return; + } + + // Check if this type matches any of our mappings + const mapping = DEFAULT_MAPPINGS.find( + m => m.name === type.typname && m.namespaceName === namespace.nspname + ); + + if (!mapping) { + return; + } + + // Create a codec for this type + // For composite types, the fromPg function extracts the first field's value + // For domain types, it just passes through the value + event.pgCodec = { + name: type.typname, + sqlType: sql.identifier(namespace.nspname, type.typname), + fromPg: (value: unknown) => { + if (value == null) { + return null; + } + // If it's already a scalar, return it + if (typeof value !== 'object' || Array.isArray(value)) { + return value; + } + // For composite types, extract the first field's value + const obj = value as Record; + const keys = Object.keys(obj); + if (keys.length > 0) { + return obj[keys[0]]; + } + // For JSON types, return the whole object + if (mapping.type === 'JSON') { + return value; + } + return value; + }, + toPg: (value: unknown) => value as string, + attributes: undefined, + executor: null, + extensions: { + oid: type._id, + pg: { + serviceName, + schemaName: namespace.nspname, + name: type.typname, + }, + tags: { + // Mark this as a custom mapped type + pgTypeMappings: mapping.type, + }, + }, + }; + }, + }, + }, + + schema: { + hooks: { + init(_, build) { + const { setGraphQLTypeForPgCodec } = build; + + // Map our custom codecs to GraphQL types + for (const codec of Object.values(build.input.pgRegistry.pgCodecs)) { + const mappingType = codec.extensions?.tags?.pgTypeMappings as string | undefined; + if (mappingType) { + const gqlTypeName = mappingType === 'JSON' ? 'JSON' : GraphQLString.name; + setGraphQLTypeForPgCodec(codec, 'input', gqlTypeName); + setGraphQLTypeForPgCodec(codec, 'output', gqlTypeName); + } + } + + return _; + }, + }, + }, +}; + +/** + * Preset that includes the PG type mappings plugin. + * + * This preset maps common custom PostgreSQL types to GraphQL scalars: + * - email -> String + * - hostname -> String + * - url -> String + * - origin -> String + * - multiple_select -> JSON + * - single_select -> JSON + */ +export const PgTypeMappingsPreset: GraphileConfig.Preset = { + plugins: [PgTypeMappingsPlugin], +}; + +export default PgTypeMappingsPlugin; diff --git a/graphile/graphile-settings/src/presets/constructive-preset.ts b/graphile/graphile-settings/src/presets/constructive-preset.ts index edba0624f..e9d03a6df 100644 --- a/graphile/graphile-settings/src/presets/constructive-preset.ts +++ b/graphile/graphile-settings/src/presets/constructive-preset.ts @@ -9,6 +9,7 @@ import { EnableAllFilterColumnsPreset } from '../plugins/enable-all-filter-colum import { ManyToManyOptInPreset } from '../plugins/many-to-many-preset'; import { MetaSchemaPreset } from '../plugins/meta-schema'; import { TsvectorCodecPreset } from '../plugins/tsvector-codec'; +import { PgTypeMappingsPreset } from '../plugins/pg-type-mappings'; /** * Constructive PostGraphile v5 Preset @@ -25,6 +26,7 @@ import { TsvectorCodecPreset } from '../plugins/tsvector-codec'; * - Connection filter plugin with all columns filterable * - Many-to-many relationships (opt-in via @behavior +manyToMany) * - Meta schema plugin (_meta query for introspection of tables, fields, indexes) + * - PG type mappings (maps custom types like email, url to GraphQL scalars) * * DISABLED PLUGINS: * - PgConnectionArgFilterBackwardRelationsPlugin (relation filters bloat the API) @@ -58,6 +60,7 @@ export const ConstructivePreset: GraphileConfig.Preset = { ManyToManyOptInPreset, MetaSchemaPreset, TsvectorCodecPreset, + PgTypeMappingsPreset, ], /** * Disable relation filter plugins from postgraphile-plugin-connection-filter. From a1055acbf6908ea79d429a3457f36fe5ec876a48 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Thu, 5 Feb 2026 05:01:55 +0000 Subject: [PATCH 2/5] refactor: remove single_select and multiple_select from pg-type-mappings - Remove single_select and multiple_select type mappings (jsonb types) - Simplify TypeMapping interface to only support String type - Remove JSON-related code from fromPg function - Keep only email, hostname, origin, url mappings --- .../src/plugins/pg-type-mappings.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/graphile/graphile-settings/src/plugins/pg-type-mappings.ts b/graphile/graphile-settings/src/plugins/pg-type-mappings.ts index 213c4cd44..f3793484f 100644 --- a/graphile/graphile-settings/src/plugins/pg-type-mappings.ts +++ b/graphile/graphile-settings/src/plugins/pg-type-mappings.ts @@ -10,8 +10,8 @@ export interface TypeMapping { name: string; /** PostgreSQL schema/namespace name */ namespaceName: string; - /** GraphQL type to map to ('String' or 'JSON') */ - type: 'String' | 'JSON'; + /** GraphQL type to map to */ + type: 'String'; } /** @@ -22,8 +22,6 @@ export interface TypeMapping { const DEFAULT_MAPPINGS: TypeMapping[] = [ { name: 'email', namespaceName: 'public', type: 'String' }, { name: 'hostname', namespaceName: 'public', type: 'String' }, - { name: 'multiple_select', namespaceName: 'public', type: 'JSON' }, - { name: 'single_select', namespaceName: 'public', type: 'JSON' }, { name: 'origin', namespaceName: 'public', type: 'String' }, { name: 'url', namespaceName: 'public', type: 'String' }, ]; @@ -97,10 +95,6 @@ export const PgTypeMappingsPlugin: GraphileConfig.Plugin = { if (keys.length > 0) { return obj[keys[0]]; } - // For JSON types, return the whole object - if (mapping.type === 'JSON') { - return value; - } return value; }, toPg: (value: unknown) => value as string, @@ -132,7 +126,7 @@ export const PgTypeMappingsPlugin: GraphileConfig.Plugin = { for (const codec of Object.values(build.input.pgRegistry.pgCodecs)) { const mappingType = codec.extensions?.tags?.pgTypeMappings as string | undefined; if (mappingType) { - const gqlTypeName = mappingType === 'JSON' ? 'JSON' : GraphQLString.name; + const gqlTypeName = GraphQLString.name; setGraphQLTypeForPgCodec(codec, 'input', gqlTypeName); setGraphQLTypeForPgCodec(codec, 'output', gqlTypeName); } @@ -152,8 +146,6 @@ export const PgTypeMappingsPlugin: GraphileConfig.Plugin = { * - hostname -> String * - url -> String * - origin -> String - * - multiple_select -> JSON - * - single_select -> JSON */ export const PgTypeMappingsPreset: GraphileConfig.Preset = { plugins: [PgTypeMappingsPlugin], From 20fe6e7ac003087a211f8abed5c39347bfef3d50 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Thu, 5 Feb 2026 05:07:10 +0000 Subject: [PATCH 3/5] refactor: update fixtures to remove single_select/multiple_select and rename to constructiveInternalType* - Remove single_select and multiple_select domain files from fixtures - Rename pgpmInternalType* to constructiveInternalType* in all domain comments - Update pgpm.plan to remove single_select/multiple_select entries --- .../deploy/schemas/public/domains/attachment.sql | 2 +- .../types/deploy/schemas/public/domains/email.sql | 2 +- .../types/deploy/schemas/public/domains/hostname.sql | 2 +- .../types/deploy/schemas/public/domains/image.sql | 2 +- .../schemas/public/domains/multiple_select.sql | 8 -------- .../types/deploy/schemas/public/domains/origin.sql | 2 +- .../deploy/schemas/public/domains/single_select.sql | 12 ------------ .../types/deploy/schemas/public/domains/upload.sql | 2 +- .../types/deploy/schemas/public/domains/url.sql | 2 +- .../simple-w-exts/extensions/@pgpm/types/pgpm.plan | 2 -- .../schemas/public/domains/multiple_select.sql | 7 ------- .../revert/schemas/public/domains/single_select.sql | 7 ------- .../schemas/public/domains/multiple_select.sql | 7 ------- .../verify/schemas/public/domains/single_select.sql | 7 ------- 14 files changed, 7 insertions(+), 57 deletions(-) delete mode 100644 __fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/multiple_select.sql delete mode 100644 __fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/single_select.sql delete mode 100644 __fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/revert/schemas/public/domains/multiple_select.sql delete mode 100644 __fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/revert/schemas/public/domains/single_select.sql delete mode 100644 __fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/verify/schemas/public/domains/multiple_select.sql delete mode 100644 __fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/verify/schemas/public/domains/single_select.sql diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/attachment.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/attachment.sql index 1dbb8dff4..2a639031b 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/attachment.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/attachment.sql @@ -3,5 +3,5 @@ BEGIN; CREATE DOMAIN attachment AS text CHECK (VALUE ~ '^(https?)://[^\s/$.?#].[^\s]*$'); -COMMENT ON DOMAIN attachment IS E'@name pgpmInternalTypeAttachment'; +COMMENT ON DOMAIN attachment IS E'@name constructiveInternalTypeAttachment'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/email.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/email.sql index d7a215359..3e56fbcef 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/email.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/email.sql @@ -3,6 +3,6 @@ BEGIN; CREATE DOMAIN email AS citext CHECK (value ~ '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$'); -COMMENT ON DOMAIN email IS E'@name pgpmInternalTypeEmail'; +COMMENT ON DOMAIN email IS E'@name constructiveInternalTypeEmail'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/hostname.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/hostname.sql index 97b83afb7..ddacb184e 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/hostname.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/hostname.sql @@ -3,6 +3,6 @@ BEGIN; CREATE DOMAIN hostname AS text CHECK (VALUE ~ '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'); -COMMENT ON DOMAIN hostname IS E'@name pgpmInternalTypeHostname'; +COMMENT ON DOMAIN hostname IS E'@name constructiveInternalTypeHostname'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/image.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/image.sql index c0cc4e043..cd47f1095 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/image.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/image.sql @@ -7,6 +7,6 @@ CREATE DOMAIN image AS jsonb CHECK ( AND value->>'url' ~ '^(https?)://[^\s/$.?#].[^\s]*$' ); -COMMENT ON DOMAIN image IS E'@name pgpmInternalTypeImage'; +COMMENT ON DOMAIN image IS E'@name constructiveInternalTypeImage'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/multiple_select.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/multiple_select.sql deleted file mode 100644 index 930161cc6..000000000 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/multiple_select.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Deploy schemas/public/domains/multiple_select to pg --- requires: schemas/public/schema - -BEGIN; -CREATE DOMAIN multiple_select AS jsonb CHECK (value ?& ARRAY['value']); -COMMENT ON DOMAIN multiple_select IS E'@name pgpmInternalTypeMultipleSelect'; -COMMIT; - diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql index 299c9fe06..83098feb3 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql @@ -3,6 +3,6 @@ BEGIN; CREATE DOMAIN origin AS text CHECK (VALUE = substring(VALUE from '^(https?://[^/]*)')); -COMMENT ON DOMAIN origin IS E'@name pgpmInternalTypeOrigin'; +COMMENT ON DOMAIN origin IS E'@name constructiveInternalTypeOrigin'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/single_select.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/single_select.sql deleted file mode 100644 index 99995462f..000000000 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/single_select.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Deploy schemas/public/domains/single_select to pg - --- requires: schemas/public/schema - -BEGIN; - -CREATE DOMAIN single_select AS jsonb CHECK ( - value ?& ARRAY['value'] -); -COMMENT ON DOMAIN single_select IS E'@name pgpmInternalTypeSingleSelect'; - -COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/upload.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/upload.sql index 5c22e4a54..6841e1eb9 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/upload.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/upload.sql @@ -9,6 +9,6 @@ CREATE DOMAIN upload AS jsonb CHECK ( AND value->>'url' ~ '^(https?)://[^\s/$.?#].[^\s]*$' ); -COMMENT ON DOMAIN upload IS E'@name pgpmInternalTypeUpload'; +COMMENT ON DOMAIN upload IS E'@name constructiveInternalTypeUpload'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql index 8c7e9b0a3..ae9d7c9c1 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql @@ -3,6 +3,6 @@ BEGIN; CREATE DOMAIN url AS text CHECK (VALUE ~ '^(https?)://[^\s/$.?#].[^\s]*$'); -COMMENT ON DOMAIN url IS E'@name pgpmInternalTypeUrl'; +COMMENT ON DOMAIN url IS E'@name constructiveInternalTypeUrl'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/pgpm.plan b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/pgpm.plan index cf700977e..c9653e3f5 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/pgpm.plan +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/pgpm.plan @@ -7,8 +7,6 @@ schemas/public/domains/attachment [schemas/public/schema] 2017-08-11T08:11:51Z s schemas/public/domains/email [schemas/public/schema] 2017-08-11T08:11:51Z skitch # add schemas/public/domains/email schemas/public/domains/hostname [schemas/public/schema] 2017-08-11T08:11:51Z skitch # add schemas/public/domains/hostname schemas/public/domains/image [schemas/public/schema] 2017-08-11T08:11:51Z skitch # add schemas/public/domains/image -schemas/public/domains/multiple_select [schemas/public/schema] 2017-08-11T08:11:51Z skitch # add schemas/public/domains/multiple_select schemas/public/domains/origin [schemas/public/schema] 2017-08-11T08:11:51Z skitch # add schemas/public/domains/origin -schemas/public/domains/single_select [schemas/public/schema] 2017-08-11T08:11:51Z skitch # add schemas/public/domains/single_select schemas/public/domains/upload [schemas/public/schema] 2017-08-11T08:11:51Z skitch # add schemas/public/domains/upload schemas/public/domains/url [schemas/public/schema] 2017-08-11T08:11:51Z skitch # add schemas/public/domains/url diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/revert/schemas/public/domains/multiple_select.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/revert/schemas/public/domains/multiple_select.sql deleted file mode 100644 index 3ba0b0f86..000000000 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/revert/schemas/public/domains/multiple_select.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert schemas/public/domains/multiple_select from pg - -BEGIN; - -DROP TYPE public.multiple_select; - -COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/revert/schemas/public/domains/single_select.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/revert/schemas/public/domains/single_select.sql deleted file mode 100644 index e08888b5c..000000000 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/revert/schemas/public/domains/single_select.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert schemas/public/domains/single_select from pg - -BEGIN; - -DROP TYPE public.single_select; - -COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/verify/schemas/public/domains/multiple_select.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/verify/schemas/public/domains/multiple_select.sql deleted file mode 100644 index bd04fe72f..000000000 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/verify/schemas/public/domains/multiple_select.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Verify schemas/public/domains/multiple_select on pg - -BEGIN; - -SELECT verify_domain ('public.multiple_select'); - -ROLLBACK; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/verify/schemas/public/domains/single_select.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/verify/schemas/public/domains/single_select.sql deleted file mode 100644 index 5f7d4c80f..000000000 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/verify/schemas/public/domains/single_select.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Verify schemas/public/domains/single_select on pg - -BEGIN; - -SELECT verify_domain ('public.single_select'); - -ROLLBACK; From 1567f59ab5cf5b3ff7589575122b46abddb44d2b Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Thu, 5 Feb 2026 06:06:40 +0000 Subject: [PATCH 4/5] refactor: simplify domain type validation in fixtures - email: remove complex regex, keep as citext - hostname: remove regex - url: remove regex - origin: remove regex - attachment: remove regex - image: jsonb requiring only 'url' key - upload: jsonb requiring at least one of 'url', 'id', or 'key' --- .../types/deploy/schemas/public/domains/attachment.sql | 2 +- .../@pgpm/types/deploy/schemas/public/domains/email.sql | 2 +- .../types/deploy/schemas/public/domains/hostname.sql | 2 +- .../@pgpm/types/deploy/schemas/public/domains/image.sql | 6 +----- .../@pgpm/types/deploy/schemas/public/domains/origin.sql | 2 +- .../@pgpm/types/deploy/schemas/public/domains/upload.sql | 9 +-------- .../@pgpm/types/deploy/schemas/public/domains/url.sql | 2 +- 7 files changed, 7 insertions(+), 18 deletions(-) diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/attachment.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/attachment.sql index 2a639031b..d477b293a 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/attachment.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/attachment.sql @@ -2,6 +2,6 @@ -- requires: schemas/public/schema BEGIN; -CREATE DOMAIN attachment AS text CHECK (VALUE ~ '^(https?)://[^\s/$.?#].[^\s]*$'); +CREATE DOMAIN attachment AS text; COMMENT ON DOMAIN attachment IS E'@name constructiveInternalTypeAttachment'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/email.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/email.sql index 3e56fbcef..4aa06afee 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/email.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/email.sql @@ -2,7 +2,7 @@ -- requires: schemas/public/schema BEGIN; -CREATE DOMAIN email AS citext CHECK (value ~ '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$'); +CREATE DOMAIN email AS citext; COMMENT ON DOMAIN email IS E'@name constructiveInternalTypeEmail'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/hostname.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/hostname.sql index ddacb184e..9505960c6 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/hostname.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/hostname.sql @@ -2,7 +2,7 @@ -- requires: schemas/public/schema BEGIN; -CREATE DOMAIN hostname AS text CHECK (VALUE ~ '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'); +CREATE DOMAIN hostname AS text; COMMENT ON DOMAIN hostname IS E'@name constructiveInternalTypeHostname'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/image.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/image.sql index cd47f1095..24e2b786c 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/image.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/image.sql @@ -2,11 +2,7 @@ -- requires: schemas/public/schema BEGIN; -CREATE DOMAIN image AS jsonb CHECK ( - value ?& ARRAY['url', 'mime'] - AND - value->>'url' ~ '^(https?)://[^\s/$.?#].[^\s]*$' -); +CREATE DOMAIN image AS jsonb CHECK (value ? 'url'); COMMENT ON DOMAIN image IS E'@name constructiveInternalTypeImage'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql index 83098feb3..f554c86fd 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql @@ -2,7 +2,7 @@ -- requires: schemas/public/schema BEGIN; -CREATE DOMAIN origin AS text CHECK (VALUE = substring(VALUE from '^(https?://[^/]*)')); +CREATE DOMAIN origin AS text; COMMENT ON DOMAIN origin IS E'@name constructiveInternalTypeOrigin'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/upload.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/upload.sql index 6841e1eb9..7a95a791c 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/upload.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/upload.sql @@ -1,14 +1,7 @@ -- Deploy schemas/public/domains/upload to pg - -- requires: schemas/public/schema BEGIN; - -CREATE DOMAIN upload AS jsonb CHECK ( - value ?& ARRAY['url', 'mime'] - AND - value->>'url' ~ '^(https?)://[^\s/$.?#].[^\s]*$' -); +CREATE DOMAIN upload AS jsonb CHECK (value ? 'url' OR value ? 'id' OR value ? 'key'); COMMENT ON DOMAIN upload IS E'@name constructiveInternalTypeUpload'; - COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql index ae9d7c9c1..8715fef4c 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql @@ -2,7 +2,7 @@ -- requires: schemas/public/schema BEGIN; -CREATE DOMAIN url AS text CHECK (VALUE ~ '^(https?)://[^\s/$.?#].[^\s]*$'); +CREATE DOMAIN url AS text; COMMENT ON DOMAIN url IS E'@name constructiveInternalTypeUrl'; COMMIT; From 0bd2af777aed17a280a784ef50f2c9ee7df3749b Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Thu, 5 Feb 2026 07:07:11 +0000 Subject: [PATCH 5/5] feat: add lenient URL regex validation to url and origin domains URL validation: ^https?://[^\s]+$ - Must start with http:// or https:// - Must have at least one character after protocol - No whitespace allowed --- .../@pgpm/types/deploy/schemas/public/domains/origin.sql | 2 +- .../@pgpm/types/deploy/schemas/public/domains/url.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql index f554c86fd..9246aac70 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/origin.sql @@ -2,7 +2,7 @@ -- requires: schemas/public/schema BEGIN; -CREATE DOMAIN origin AS text; +CREATE DOMAIN origin AS text CHECK (value ~ '^https?://[^\s]+$'); COMMENT ON DOMAIN origin IS E'@name constructiveInternalTypeOrigin'; COMMIT; diff --git a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql index 8715fef4c..5e5a94367 100644 --- a/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql +++ b/__fixtures__/sqitch/simple-w-exts/extensions/@pgpm/types/deploy/schemas/public/domains/url.sql @@ -2,7 +2,7 @@ -- requires: schemas/public/schema BEGIN; -CREATE DOMAIN url AS text; +CREATE DOMAIN url AS text CHECK (value ~ '^https?://[^\s]+$'); COMMENT ON DOMAIN url IS E'@name constructiveInternalTypeUrl'; COMMIT;