diff --git a/packages/achievements/deploy/schemas/status_public/tables/level_requirements/table.sql b/packages/achievements/deploy/schemas/status_public/tables/level_requirements/table.sql index 95c80f822..b762b7036 100644 --- a/packages/achievements/deploy/schemas/status_public/tables/level_requirements/table.sql +++ b/packages/achievements/deploy/schemas/status_public/tables/level_requirements/table.sql @@ -15,6 +15,12 @@ CREATE TABLE status_public.level_requirements ( ); COMMENT ON TABLE status_public.level_requirements IS 'Requirements to achieve a level'; +COMMENT ON COLUMN status_public.level_requirements.id IS 'Unique identifier for this requirement'; +COMMENT ON COLUMN status_public.level_requirements.name IS 'Requirement name (e.g. posts_created, logins); matches user_steps.name'; +COMMENT ON COLUMN status_public.level_requirements.level IS 'Level this requirement belongs to (references levels.name)'; +COMMENT ON COLUMN status_public.level_requirements.required_count IS 'Number of steps needed to satisfy this requirement (default 1)'; +COMMENT ON COLUMN status_public.level_requirements.priority IS 'Display/evaluation order; lower numbers are checked first (default 100)'; + CREATE INDEX ON status_public.level_requirements (name, level, priority); GRANT SELECT ON TABLE status_public.levels TO authenticated; diff --git a/packages/achievements/deploy/schemas/status_public/tables/levels/table.sql b/packages/achievements/deploy/schemas/status_public/tables/levels/table.sql index e39c966d8..077ba3ae6 100644 --- a/packages/achievements/deploy/schemas/status_public/tables/levels/table.sql +++ b/packages/achievements/deploy/schemas/status_public/tables/levels/table.sql @@ -9,6 +9,7 @@ CREATE TABLE status_public.levels ( ); COMMENT ON TABLE status_public.levels IS 'Levels for achievement'; +COMMENT ON COLUMN status_public.levels.name IS 'Unique level name used as the primary key (e.g. bronze, silver, gold)'; GRANT SELECT ON TABLE status_public.levels TO public; diff --git a/packages/achievements/deploy/schemas/status_public/tables/user_achievements/table.sql b/packages/achievements/deploy/schemas/status_public/tables/user_achievements/table.sql index 5796121a4..0f9e9756d 100644 --- a/packages/achievements/deploy/schemas/status_public/tables/user_achievements/table.sql +++ b/packages/achievements/deploy/schemas/status_public/tables/user_achievements/table.sql @@ -14,6 +14,11 @@ CREATE TABLE status_public.user_achievements ( ); COMMENT ON TABLE status_public.user_achievements IS 'This table represents the users progress for particular level requirements, tallying the total count. This table is updated via triggers and should not be updated maually.'; +COMMENT ON COLUMN status_public.user_achievements.id IS 'Unique identifier for this achievement progress record'; +COMMENT ON COLUMN status_public.user_achievements.user_id IS 'User whose progress is being tracked'; +COMMENT ON COLUMN status_public.user_achievements.name IS 'Name of the level requirement this progress relates to'; +COMMENT ON COLUMN status_public.user_achievements.count IS 'Accumulated count toward the requirement (updated by triggers)'; +COMMENT ON COLUMN status_public.user_achievements.created_at IS 'Timestamp when this progress record was first created'; CREATE INDEX ON status_public.user_achievements (user_id, name); diff --git a/packages/achievements/deploy/schemas/status_public/tables/user_steps/table.sql b/packages/achievements/deploy/schemas/status_public/tables/user_steps/table.sql index 2c1791f9b..3164a30c8 100644 --- a/packages/achievements/deploy/schemas/status_public/tables/user_steps/table.sql +++ b/packages/achievements/deploy/schemas/status_public/tables/user_steps/table.sql @@ -13,6 +13,12 @@ CREATE TABLE status_public.user_steps ( ); COMMENT ON TABLE status_public.user_steps IS 'The user achieving a requirement for a level. Log table that has every single step ever taken.'; +COMMENT ON COLUMN status_public.user_steps.id IS 'Unique identifier for this step record'; +COMMENT ON COLUMN status_public.user_steps.user_id IS 'User who performed this step'; +COMMENT ON COLUMN status_public.user_steps.name IS 'Name of the level requirement this step counts toward'; +COMMENT ON COLUMN status_public.user_steps.count IS 'Number of units this step contributes (default 1)'; +COMMENT ON COLUMN status_public.user_steps.created_at IS 'Timestamp when this step was recorded'; + CREATE INDEX ON status_public.user_steps (user_id, name); COMMIT; diff --git a/packages/achievements/sql/pgpm-achievements--0.15.3.sql b/packages/achievements/sql/pgpm-achievements--0.15.3.sql index acd543a88..07b94c52a 100644 --- a/packages/achievements/sql/pgpm-achievements--0.15.3.sql +++ b/packages/achievements/sql/pgpm-achievements--0.15.3.sql @@ -22,6 +22,11 @@ CREATE TABLE status_public.user_steps ( ); COMMENT ON TABLE status_public.user_steps IS 'The user achieving a requirement for a level. Log table that has every single step ever taken.'; +COMMENT ON COLUMN status_public.user_steps.id IS 'Unique identifier for this step record'; +COMMENT ON COLUMN status_public.user_steps.user_id IS 'User who performed this step'; +COMMENT ON COLUMN status_public.user_steps.name IS 'Name of the level requirement this step counts toward'; +COMMENT ON COLUMN status_public.user_steps.count IS 'Number of units this step contributes (default 1)'; +COMMENT ON COLUMN status_public.user_steps.created_at IS 'Timestamp when this step was recorded'; CREATE INDEX ON status_public.user_steps (user_id, name); @@ -124,6 +129,11 @@ CREATE TABLE status_public.user_achievements ( ); COMMENT ON TABLE status_public.user_achievements IS 'This table represents the users progress for particular level requirements, tallying the total count. This table is updated via triggers and should not be updated maually.'; +COMMENT ON COLUMN status_public.user_achievements.id IS 'Unique identifier for this achievement progress record'; +COMMENT ON COLUMN status_public.user_achievements.user_id IS 'User whose progress is being tracked'; +COMMENT ON COLUMN status_public.user_achievements.name IS 'Name of the level requirement this progress relates to'; +COMMENT ON COLUMN status_public.user_achievements.count IS 'Accumulated count toward the requirement (updated by triggers)'; +COMMENT ON COLUMN status_public.user_achievements.created_at IS 'Timestamp when this progress record was first created'; CREATE INDEX ON status_public.user_achievements (user_id, name); @@ -145,6 +155,7 @@ CREATE TABLE status_public.levels ( ); COMMENT ON TABLE status_public.levels IS 'Levels for achievement'; +COMMENT ON COLUMN status_public.levels.name IS 'Unique level name used as the primary key (e.g. bronze, silver, gold)'; GRANT SELECT ON status_public.levels TO PUBLIC; @@ -158,6 +169,11 @@ CREATE TABLE status_public.level_requirements ( ); COMMENT ON TABLE status_public.level_requirements IS 'Requirements to achieve a level'; +COMMENT ON COLUMN status_public.level_requirements.id IS 'Unique identifier for this requirement'; +COMMENT ON COLUMN status_public.level_requirements.name IS 'Requirement name (e.g. posts_created, logins); matches user_steps.name'; +COMMENT ON COLUMN status_public.level_requirements.level IS 'Level this requirement belongs to (references levels.name)'; +COMMENT ON COLUMN status_public.level_requirements.required_count IS 'Number of steps needed to satisfy this requirement (default 1)'; +COMMENT ON COLUMN status_public.level_requirements.priority IS 'Display/evaluation order; lower numbers are checked first (default 100)'; CREATE INDEX ON status_public.level_requirements (name, level, priority); @@ -261,4 +277,4 @@ CREATE TRIGGER update_achievements_tg AFTER INSERT ON status_public.user_steps FOR EACH ROW - EXECUTE PROCEDURE status_private.tg_update_achievements_tg(); \ No newline at end of file + EXECUTE PROCEDURE status_private.tg_update_achievements_tg(); diff --git a/packages/database-jobs/deploy/schemas/app_jobs/tables/job_queues/table.sql b/packages/database-jobs/deploy/schemas/app_jobs/tables/job_queues/table.sql index 4ad1305c9..dd003c348 100644 --- a/packages/database-jobs/deploy/schemas/app_jobs/tables/job_queues/table.sql +++ b/packages/database-jobs/deploy/schemas/app_jobs/tables/job_queues/table.sql @@ -8,5 +8,12 @@ CREATE TABLE app_jobs.job_queues ( locked_at timestamptz, locked_by text ); + +COMMENT ON TABLE app_jobs.job_queues IS 'Queue metadata: tracks job counts and locking state for each named queue'; +COMMENT ON COLUMN app_jobs.job_queues.queue_name IS 'Unique name identifying this queue'; +COMMENT ON COLUMN app_jobs.job_queues.job_count IS 'Number of pending jobs in this queue'; +COMMENT ON COLUMN app_jobs.job_queues.locked_at IS 'Timestamp when this queue was locked for batch processing'; +COMMENT ON COLUMN app_jobs.job_queues.locked_by IS 'Identifier of the worker that currently holds the queue lock'; + COMMIT; diff --git a/packages/database-jobs/deploy/schemas/app_jobs/tables/jobs/table.sql b/packages/database-jobs/deploy/schemas/app_jobs/tables/jobs/table.sql index b5e27f5da..48ea7c146 100644 --- a/packages/database-jobs/deploy/schemas/app_jobs/tables/jobs/table.sql +++ b/packages/database-jobs/deploy/schemas/app_jobs/tables/jobs/table.sql @@ -23,5 +23,21 @@ CREATE TABLE app_jobs.jobs ( CHECK (length(locked_by) > 3), UNIQUE (key) ); + +COMMENT ON TABLE app_jobs.jobs IS 'Background job queue with database scoping: each row is a pending or in-progress task for a specific database'; +COMMENT ON COLUMN app_jobs.jobs.id IS 'Auto-incrementing job identifier'; +COMMENT ON COLUMN app_jobs.jobs.database_id IS 'Database this job belongs to, for multi-tenant job isolation'; +COMMENT ON COLUMN app_jobs.jobs.queue_name IS 'Name of the queue this job belongs to; used for worker routing and concurrency control'; +COMMENT ON COLUMN app_jobs.jobs.task_identifier IS 'Identifier for the task type (maps to a worker handler function)'; +COMMENT ON COLUMN app_jobs.jobs.payload IS 'JSON payload of arguments passed to the task handler'; +COMMENT ON COLUMN app_jobs.jobs.priority IS 'Execution priority; lower numbers run first (default 0)'; +COMMENT ON COLUMN app_jobs.jobs.run_at IS 'Earliest time this job should be executed; used for delayed/scheduled execution'; +COMMENT ON COLUMN app_jobs.jobs.attempts IS 'Number of times this job has been attempted so far'; +COMMENT ON COLUMN app_jobs.jobs.max_attempts IS 'Maximum retry attempts before the job is considered permanently failed'; +COMMENT ON COLUMN app_jobs.jobs.key IS 'Optional unique deduplication key; prevents duplicate jobs with the same key'; +COMMENT ON COLUMN app_jobs.jobs.last_error IS 'Error message from the most recent failed attempt'; +COMMENT ON COLUMN app_jobs.jobs.locked_at IS 'Timestamp when a worker locked this job for processing'; +COMMENT ON COLUMN app_jobs.jobs.locked_by IS 'Identifier of the worker that currently holds the lock'; + COMMIT; diff --git a/packages/database-jobs/deploy/schemas/app_jobs/tables/scheduled_jobs/table.sql b/packages/database-jobs/deploy/schemas/app_jobs/tables/scheduled_jobs/table.sql index 2f1506fb9..75d81c9e3 100644 --- a/packages/database-jobs/deploy/schemas/app_jobs/tables/scheduled_jobs/table.sql +++ b/packages/database-jobs/deploy/schemas/app_jobs/tables/scheduled_jobs/table.sql @@ -23,5 +23,21 @@ CREATE TABLE app_jobs.scheduled_jobs ( CHECK (length(locked_by) > 3), UNIQUE (key) ); + +COMMENT ON TABLE app_jobs.scheduled_jobs IS 'Recurring/cron-style job definitions with database scoping: each row spawns jobs on a schedule for a specific database'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.id IS 'Auto-incrementing scheduled job identifier'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.database_id IS 'Database this scheduled job belongs to, for multi-tenant isolation'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.queue_name IS 'Name of the queue spawned jobs are placed into'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.task_identifier IS 'Task type identifier for spawned jobs'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.payload IS 'JSON payload passed to each spawned job'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.priority IS 'Priority assigned to spawned jobs (lower = higher priority)'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.max_attempts IS 'Max retry attempts for spawned jobs'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.key IS 'Optional unique deduplication key'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.locked_at IS 'Timestamp when the scheduler locked this record for processing'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.locked_by IS 'Identifier of the scheduler worker holding the lock'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.schedule_info IS 'JSON schedule configuration (e.g. cron expression, interval)'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.last_scheduled IS 'Timestamp when a job was last spawned from this schedule'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.last_scheduled_id IS 'ID of the last job spawned from this schedule'; + COMMIT; diff --git a/packages/database-jobs/sql/pgpm-database-jobs--0.15.3.sql b/packages/database-jobs/sql/pgpm-database-jobs--0.15.3.sql index f8dff738b..73c98278a 100644 --- a/packages/database-jobs/sql/pgpm-database-jobs--0.15.3.sql +++ b/packages/database-jobs/sql/pgpm-database-jobs--0.15.3.sql @@ -134,6 +134,21 @@ CREATE TABLE app_jobs.scheduled_jobs ( UNIQUE (key) ); +COMMENT ON TABLE app_jobs.scheduled_jobs IS 'Recurring/cron-style job definitions with database scoping: each row spawns jobs on a schedule for a specific database'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.id IS 'Auto-incrementing scheduled job identifier'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.database_id IS 'Database this scheduled job belongs to, for multi-tenant isolation'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.queue_name IS 'Name of the queue spawned jobs are placed into'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.task_identifier IS 'Task type identifier for spawned jobs'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.payload IS 'JSON payload passed to each spawned job'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.priority IS 'Priority assigned to spawned jobs (lower = higher priority)'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.max_attempts IS 'Max retry attempts for spawned jobs'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.key IS 'Optional unique deduplication key'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.locked_at IS 'Timestamp when the scheduler locked this record for processing'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.locked_by IS 'Identifier of the scheduler worker holding the lock'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.schedule_info IS 'JSON schedule configuration (e.g. cron expression, interval)'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.last_scheduled IS 'Timestamp when a job was last spawned from this schedule'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.last_scheduled_id IS 'ID of the last job spawned from this schedule'; + CREATE FUNCTION app_jobs.do_notify() RETURNS trigger AS $EOFCODE$ BEGIN PERFORM @@ -176,6 +191,21 @@ CREATE TABLE app_jobs.jobs ( UNIQUE (key) ); +COMMENT ON TABLE app_jobs.jobs IS 'Background job queue with database scoping: each row is a pending or in-progress task for a specific database'; +COMMENT ON COLUMN app_jobs.jobs.id IS 'Auto-incrementing job identifier'; +COMMENT ON COLUMN app_jobs.jobs.database_id IS 'Database this job belongs to, for multi-tenant job isolation'; +COMMENT ON COLUMN app_jobs.jobs.queue_name IS 'Name of the queue this job belongs to; used for worker routing and concurrency control'; +COMMENT ON COLUMN app_jobs.jobs.task_identifier IS 'Identifier for the task type (maps to a worker handler function)'; +COMMENT ON COLUMN app_jobs.jobs.payload IS 'JSON payload of arguments passed to the task handler'; +COMMENT ON COLUMN app_jobs.jobs.priority IS 'Execution priority; lower numbers run first (default 0)'; +COMMENT ON COLUMN app_jobs.jobs.run_at IS 'Earliest time this job should be executed; used for delayed/scheduled execution'; +COMMENT ON COLUMN app_jobs.jobs.attempts IS 'Number of times this job has been attempted so far'; +COMMENT ON COLUMN app_jobs.jobs.max_attempts IS 'Maximum retry attempts before the job is considered permanently failed'; +COMMENT ON COLUMN app_jobs.jobs.key IS 'Optional unique deduplication key; prevents duplicate jobs with the same key'; +COMMENT ON COLUMN app_jobs.jobs.last_error IS 'Error message from the most recent failed attempt'; +COMMENT ON COLUMN app_jobs.jobs.locked_at IS 'Timestamp when a worker locked this job for processing'; +COMMENT ON COLUMN app_jobs.jobs.locked_by IS 'Identifier of the worker that currently holds the lock'; + ALTER TABLE app_jobs.jobs ADD COLUMN created_at timestamptz; @@ -275,6 +305,12 @@ CREATE TABLE app_jobs.job_queues ( locked_by text ); +COMMENT ON TABLE app_jobs.job_queues IS 'Queue metadata: tracks job counts and locking state for each named queue'; +COMMENT ON COLUMN app_jobs.job_queues.queue_name IS 'Unique name identifying this queue'; +COMMENT ON COLUMN app_jobs.job_queues.job_count IS 'Number of pending jobs in this queue'; +COMMENT ON COLUMN app_jobs.job_queues.locked_at IS 'Timestamp when this queue was locked for batch processing'; +COMMENT ON COLUMN app_jobs.job_queues.locked_by IS 'Identifier of the worker that currently holds the queue lock'; + CREATE INDEX job_queues_locked_by_idx ON app_jobs.job_queues (locked_by); GRANT SELECT, INSERT, UPDATE, DELETE ON app_jobs.job_queues TO administrator; @@ -766,4 +802,4 @@ BEGIN RETURN v_job; END; -$EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; \ No newline at end of file +$EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; diff --git a/packages/encrypted-secrets-table/deploy/schemas/secrets_schema/tables/secrets_table/table.sql b/packages/encrypted-secrets-table/deploy/schemas/secrets_schema/tables/secrets_table/table.sql index 50f6a9356..3466fdc0c 100644 --- a/packages/encrypted-secrets-table/deploy/schemas/secrets_schema/tables/secrets_table/table.sql +++ b/packages/encrypted-secrets-table/deploy/schemas/secrets_schema/tables/secrets_table/table.sql @@ -13,4 +13,11 @@ CREATE TABLE secrets_schema.secrets_table ( UNIQUE(secrets_owned_field, name) ); +COMMENT ON TABLE secrets_schema.secrets_table IS 'Encrypted key-value secret storage: stores secrets as either raw bytea or encrypted text, scoped to an owning entity'; +COMMENT ON COLUMN secrets_schema.secrets_table.id IS 'Unique identifier for this secret'; +COMMENT ON COLUMN secrets_schema.secrets_table.secrets_owned_field IS 'UUID of the owning entity (e.g. user, organization); combined with name forms a unique key'; +COMMENT ON COLUMN secrets_schema.secrets_table.name IS 'Name/key for this secret within its owner scope'; +COMMENT ON COLUMN secrets_schema.secrets_table.secrets_value_field IS 'Raw binary secret value (mutually exclusive with secrets_enc_field)'; +COMMENT ON COLUMN secrets_schema.secrets_table.secrets_enc_field IS 'Encrypted text secret value (mutually exclusive with secrets_value_field)'; + COMMIT; diff --git a/packages/encrypted-secrets-table/sql/pgpm-encrypted-secrets-table--0.15.3.sql b/packages/encrypted-secrets-table/sql/pgpm-encrypted-secrets-table--0.15.3.sql index a568c8c86..a9c317cfd 100644 --- a/packages/encrypted-secrets-table/sql/pgpm-encrypted-secrets-table--0.15.3.sql +++ b/packages/encrypted-secrets-table/sql/pgpm-encrypted-secrets-table--0.15.3.sql @@ -10,6 +10,13 @@ CREATE TABLE secrets_schema.secrets_table ( UNIQUE (secrets_owned_field, name) ); +COMMENT ON TABLE secrets_schema.secrets_table IS 'Encrypted key-value secret storage: stores secrets as either raw bytea or encrypted text, scoped to an owning entity'; +COMMENT ON COLUMN secrets_schema.secrets_table.id IS 'Unique identifier for this secret'; +COMMENT ON COLUMN secrets_schema.secrets_table.secrets_owned_field IS 'UUID of the owning entity (e.g. user, organization); combined with name forms a unique key'; +COMMENT ON COLUMN secrets_schema.secrets_table.name IS 'Name/key for this secret within its owner scope'; +COMMENT ON COLUMN secrets_schema.secrets_table.secrets_value_field IS 'Raw binary secret value (mutually exclusive with secrets_enc_field)'; +COMMENT ON COLUMN secrets_schema.secrets_table.secrets_enc_field IS 'Encrypted text secret value (mutually exclusive with secrets_value_field)'; + CREATE FUNCTION secrets_schema.tg_hash_secrets() RETURNS trigger AS $EOFCODE$ BEGIN IF (NEW.secrets_enc_field = 'crypt') THEN @@ -34,4 +41,4 @@ CREATE TRIGGER hash_secrets_insert BEFORE INSERT ON secrets_schema.secrets_table FOR EACH ROW - EXECUTE PROCEDURE secrets_schema.tg_hash_secrets(); \ No newline at end of file + EXECUTE PROCEDURE secrets_schema.tg_hash_secrets(); diff --git a/packages/inflection/README.md b/packages/inflection/README.md index f7ffb998c..f9dbfdf2f 100644 --- a/packages/inflection/README.md +++ b/packages/inflection/README.md @@ -297,7 +297,7 @@ SELECT slug FROM blog_posts; ## Integration Examples -### With @pgpm/metaschema-schema +### With @pgpm/db-meta-schema Use inflection for schema introspection and code generation: diff --git a/packages/inflection/__tests__/inflection.test.ts b/packages/inflection/__tests__/inflection.test.ts index 04efb53e4..d1bb15c9b 100644 --- a/packages/inflection/__tests__/inflection.test.ts +++ b/packages/inflection/__tests__/inflection.test.ts @@ -154,7 +154,26 @@ describe('inflection', () => { { name: 'children', result: 'children' }, { name: 'child', result: 'children' }, { name: 'man', result: 'men' }, - { name: 'men', result: 'men' } + { name: 'men', result: 'men' }, + // node.inflection v3 sync: octopus/virus use -uses + { name: 'octopus', result: 'octopuses' }, + { name: 'virus', result: 'viruses' }, + { name: 'octopuses', result: 'octopuses' }, + { name: 'viruses', result: 'viruses' }, + // node.inflection v3 sync: drive, focus, bonus, database + { name: 'drive', result: 'drives' }, + { name: 'drives', result: 'drives' }, + { name: 'focus', result: 'focuses' }, + { name: 'bonus', result: 'bonuses' }, + { name: 'database', result: 'databases' }, + { name: 'databases', result: 'databases' }, + // uncountable words + { name: 'sheep', result: 'sheep' }, + { name: 'equipment', result: 'equipment' }, + { name: 'information', result: 'information' }, + { name: 'deer', result: 'deer' }, + { name: 'series', result: 'series' }, + { name: 'species', result: 'species' } ] ); @@ -175,7 +194,31 @@ describe('inflection', () => { { name: 'children', result: 'child' }, { name: 'child', result: 'child' }, { name: 'man', result: 'man' }, - { name: 'men', result: 'man' } + { name: 'men', result: 'man' }, + // node.inflection v3 sync: octopus/virus use -uses + { name: 'octopuses', result: 'octopus' }, + { name: 'viruses', result: 'virus' }, + { name: 'octopus', result: 'octopus' }, + { name: 'virus', result: 'virus' }, + // node.inflection v3 sync: drive, database + { name: 'drives', result: 'drive' }, + { name: 'drive', result: 'drive' }, + { name: 'databases', result: 'database' }, + { name: 'database', result: 'database' }, + { name: 'bonuses', result: 'bonus' }, + // Latin suffix overrides (PostGraphile-compatible) + { name: 'schemata', result: 'schema' }, + { name: 'phenomena', result: 'phenomenon' }, + { name: 'memoranda', result: 'memorandum' }, + { name: 'curricula', result: 'curriculum' }, + { name: 'criteria', result: 'criterion' }, + { name: 'media', result: 'medium' }, + { name: 'data', result: 'datum' }, + { name: 'strata', result: 'stratum' }, + // uncountable words + { name: 'sheep', result: 'sheep' }, + { name: 'equipment', result: 'equipment' }, + { name: 'information', result: 'information' } ] ); }); diff --git a/packages/inflection/deploy/schemas/inflection/procedures/plural.sql b/packages/inflection/deploy/schemas/inflection/procedures/plural.sql index 582d4bfd6..83f812eb1 100644 --- a/packages/inflection/deploy/schemas/inflection/procedures/plural.sql +++ b/packages/inflection/deploy/schemas/inflection/procedures/plural.sql @@ -2,6 +2,7 @@ -- requires: schemas/inflection/schema -- requires: schemas/inflection/tables/inflection_rules/table +-- requires: schemas/inflection/procedures/should_skip_uncountable BEGIN; @@ -12,6 +13,10 @@ DECLARE result record; matches text[]; BEGIN + IF inflection.should_skip_uncountable(lower(str)) THEN + return str; + END IF; + FOR result IN SELECT * FROM inflection.inflection_rules where type='plural' LOOP diff --git a/packages/inflection/deploy/schemas/inflection/procedures/singular.sql b/packages/inflection/deploy/schemas/inflection/procedures/singular.sql index 56c860009..e9ee3a0f7 100644 --- a/packages/inflection/deploy/schemas/inflection/procedures/singular.sql +++ b/packages/inflection/deploy/schemas/inflection/procedures/singular.sql @@ -2,6 +2,7 @@ -- requires: schemas/inflection/schema -- requires: schemas/inflection/tables/inflection_rules/table +-- requires: schemas/inflection/procedures/should_skip_uncountable BEGIN; @@ -12,6 +13,10 @@ DECLARE result record; matches text[]; BEGIN + IF inflection.should_skip_uncountable(lower(str)) THEN + return str; + END IF; + FOR result IN SELECT * FROM inflection.inflection_rules where type='singular' LOOP diff --git a/packages/inflection/deploy/schemas/inflection/tables/inflection_rules/fixtures/1589249334312_fixture.sql b/packages/inflection/deploy/schemas/inflection/tables/inflection_rules/fixtures/1589249334312_fixture.sql index f9f54820b..7f4ab4eda 100644 --- a/packages/inflection/deploy/schemas/inflection/tables/inflection_rules/fixtures/1589249334312_fixture.sql +++ b/packages/inflection/deploy/schemas/inflection/tables/inflection_rules/fixtures/1589249334312_fixture.sql @@ -7,11 +7,14 @@ BEGIN; INSERT INTO inflection.inflection_rules (type, test, replacement) VALUES + -- plural guards: already-plural words return as-is (NULL replacement) ('plural', '^(m|wom)en$', NULL), ('plural', '(pe)ople$', NULL), ('plural', '(child)ren$', NULL), ('plural', '([ti])a$', NULL), ('plural', '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$', NULL), + ('plural', '(database)s$', NULL), + ('plural', '(drive)s$', NULL), ('plural', '(hi|ti)ves$', NULL), ('plural', '(curve)s$', NULL), ('plural', '([lr])ves$', NULL), @@ -25,11 +28,12 @@ INSERT INTO inflection.inflection_rules ('plural', '(o)es$', NULL), ('plural', '(shoe)s$', NULL), ('plural', '(cris|ax|test)es$', NULL), - ('plural', '(octop|vir)i$', NULL), + ('plural', '(octop|vir)uses$', NULL), ('plural', '(alias|canvas|status|campus)es$', NULL), - ('plural', '^(summons)es$', NULL), + ('plural', '^(summons|bonus)es$', NULL), ('plural', '^(ox)en', NULL), ('plural', '(matr)ices$', NULL), + ('plural', '(vert|ind)ices$', NULL), ('plural', '^feet$', NULL), ('plural', '^teeth$', NULL), ('plural', '^geese$', NULL), @@ -37,19 +41,22 @@ INSERT INTO inflection.inflection_rules ('plural', '^(whereas)es$', NULL), ('plural', '^(criteri)a$', NULL), ('plural', '^genera$', NULL), + -- plural replacement rules ('plural', '^(m|wom)an$', E'\\1en'), ('plural', '(pe)rson$', E'\\1ople'), ('plural', '(child)$', E'\\1ren'), + ('plural', '(drive)$', E'\\1s'), ('plural', '^(ox)$', E'\\1en'), ('plural', '(ax|test)is$', E'\\1es'), - ('plural', '(octop|vir)us$', E'\\1i'), + ('plural', '(octop|vir)us$', E'\\1uses'), ('plural', '(alias|status|canvas|campus)$', E'\\1es'), - ('plural', '^(summons)$', E'\\1es'), + ('plural', '^(summons|bonus)$', E'\\1es'), ('plural', '(bu)s$', E'\\1ses'), ('plural', '(buffal|tomat|potat)o$', E'\\1oes'), ('plural', '([ti])um$', E'\\1a'), ('plural', 'sis$', E'ses'), ('plural', '(?:([^f])fe|([lr])f)$', E'\\1\\2ves'), + ('plural', '^(focus)$', E'\\1es'), ('plural', '(hi|ti)ve$', E'\\1ves'), ('plural', '([^aeiouy]|qu)y$', E'\\1ies'), ('plural', '(matr)ix$', E'\\1ices'), @@ -65,23 +72,27 @@ INSERT INTO inflection.inflection_rules ('plural', '^genus$', E'genera'), ('plural', 's$', E's'), ('plural', '$', E's'), + -- singular guards: already-singular words return as-is (NULL replacement) ('singular', '^(m|wom)an$', NULL), ('singular', '(pe)rson$', NULL), ('singular', '(child)$', NULL), + ('singular', '(drive)$', NULL), ('singular', '^(ox)$', NULL), ('singular', '(ax|test)is$', NULL), ('singular', '(octop|vir)us$', NULL), ('singular', '(alias|status|canvas|campus)$', NULL), - ('singular', '^(summons)$', NULL), + ('singular', '^(summons|bonus)$', NULL), ('singular', '(bu)s$', NULL), ('singular', '(buffal|tomat|potat)o$', NULL), ('singular', '([ti])um$', NULL), ('singular', 'sis$', NULL), ('singular', '(?:([^f])fe|([lr])f)$', NULL), + ('singular', '^(focus)$', NULL), ('singular', '(hi|ti)ve$', NULL), ('singular', '([^aeiouy]|qu)y$', NULL), ('singular', '(x|ch|ss|sh)$', NULL), ('singular', '(matr)ix$', NULL), + ('singular', '(vert|ind)ex$', NULL), ('singular', '([m|l])ouse$', NULL), ('singular', '^foot$', NULL), ('singular', '^tooth$', NULL), @@ -90,11 +101,19 @@ INSERT INTO inflection.inflection_rules ('singular', '^(whereas)$', NULL), ('singular', '^(criteri)on$', NULL), ('singular', '^genus$', NULL), + -- singular replacement rules ('singular', '^(m|wom)en$', E'\\1an'), ('singular', '(pe)ople$', E'\\1rson'), ('singular', '(child)ren$', E'\\1'), + ('singular', '(database)s$', E'\\1'), + ('singular', '(drive)s$', E'\\1'), ('singular', '^genera$', E'genus'), ('singular', '^(criteri)a$', E'\\1on'), + -- Latin suffix overrides (PostGraphile-compatible) + ('singular', '(schema)ta$', E'\\1'), + ('singular', '(phenomen)a$', E'\\1on'), + ('singular', '(memorand)a$', E'\\1um'), + ('singular', '(curricul)a$', E'\\1um'), ('singular', '([ti])a$', E'\\1um'), ('singular', '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$', E'\\1\\2sis'), ('singular', '(hi|ti)ves$', E'\\1ve'), @@ -111,9 +130,9 @@ INSERT INTO inflection.inflection_rules ('singular', '(o)es$', E'\\1'), ('singular', '(shoe)s$', E'\\1'), ('singular', '(cris|ax|test)es$', E'\\1is'), - ('singular', '(octop|vir)i$', E'\\1us'), + ('singular', '(octop|vir)uses$', E'\\1us'), ('singular', '(alias|canvas|status|campus)es$', E'\\1'), - ('singular', '^(summons)es$', E'\\1'), + ('singular', '^(summons|bonus)es$', E'\\1'), ('singular', '^(ox)en', E'\\1'), ('singular', '(matr)ices$', E'\\1ix'), ('singular', '(vert|ind)ices$', E'\\1ex'), diff --git a/packages/inflection/pgpm.plan b/packages/inflection/pgpm.plan index 46a8404bb..d289d21f8 100644 --- a/packages/inflection/pgpm.plan +++ b/packages/inflection/pgpm.plan @@ -11,10 +11,10 @@ schemas/inflection/procedures/camel [schemas/inflection/schema schemas/inflectio schemas/inflection/procedures/dashed [schemas/inflection/schema schemas/inflection/procedures/underscore] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/dashed schemas/inflection/procedures/pascal [schemas/inflection/schema schemas/inflection/procedures/camel] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/pascal schemas/inflection/tables/inflection_rules/table [schemas/inflection/schema] 2017-08-11T08:11:51Z skitch # add schemas/inflection/tables/inflection_rules/table -schemas/inflection/procedures/plural [schemas/inflection/schema schemas/inflection/tables/inflection_rules/table] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/plural schemas/inflection/procedures/uncountable_words [schemas/inflection/schema] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/uncountable_words schemas/inflection/procedures/should_skip_uncountable [schemas/inflection/schema schemas/inflection/procedures/uncountable_words] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/should_skip_uncountable -schemas/inflection/procedures/singular [schemas/inflection/schema schemas/inflection/tables/inflection_rules/table] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/singular +schemas/inflection/procedures/plural [schemas/inflection/schema schemas/inflection/tables/inflection_rules/table schemas/inflection/procedures/should_skip_uncountable] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/plural +schemas/inflection/procedures/singular [schemas/inflection/schema schemas/inflection/tables/inflection_rules/table schemas/inflection/procedures/should_skip_uncountable] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/singular schemas/inflection/procedures/slugify [schemas/inflection/schema] 2017-08-11T08:11:51Z skitch # add schemas/inflection/procedures/slugify schemas/inflection/tables/inflection_rules/fixtures/1589249334312_fixture [schemas/inflection/schema schemas/inflection/tables/inflection_rules/table] 2017-08-11T08:11:51Z skitch # add schemas/inflection/tables/inflection_rules/fixtures/1589249334312_fixture -schemas/inflection/tables/inflection_rules/indexes/inflection_rules_type_idx [schemas/inflection/schema schemas/inflection/tables/inflection_rules/table] 2017-08-11T08:11:51Z skitch # add schemas/inflection/tables/inflection_rules/indexes/inflection_rules_type_idx \ No newline at end of file +schemas/inflection/tables/inflection_rules/indexes/inflection_rules_type_idx [schemas/inflection/schema schemas/inflection/tables/inflection_rules/table] 2017-08-11T08:11:51Z skitch # add schemas/inflection/tables/inflection_rules/indexes/inflection_rules_type_idx diff --git a/packages/inflection/sql/pgpm-inflection--0.15.3.sql b/packages/inflection/sql/pgpm-inflection--0.15.3.sql index 14e7bdb88..fef389ef4 100644 --- a/packages/inflection/sql/pgpm-inflection--0.15.3.sql +++ b/packages/inflection/sql/pgpm-inflection--0.15.3.sql @@ -242,6 +242,10 @@ DECLARE result record; matches text[]; BEGIN + IF inflection.should_skip_uncountable(lower(str)) THEN + return str; + END IF; + FOR result IN SELECT * FROM inflection.inflection_rules where type='plural' LOOP @@ -272,6 +276,10 @@ DECLARE result record; matches text[]; BEGIN + IF inflection.should_skip_uncountable(lower(str)) THEN + return str; + END IF; + FOR result IN SELECT * FROM inflection.inflection_rules where type='singular' LOOP @@ -334,11 +342,14 @@ INSERT INTO inflection.inflection_rules ( test, replacement ) VALUES + -- plural guards: already-plural words return as-is (NULL replacement) ('plural', '^(m|wom)en$', NULL), ('plural', '(pe)ople$', NULL), ('plural', '(child)ren$', NULL), ('plural', '([ti])a$', NULL), ('plural', '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$', NULL), + ('plural', '(database)s$', NULL), + ('plural', '(drive)s$', NULL), ('plural', '(hi|ti)ves$', NULL), ('plural', '(curve)s$', NULL), ('plural', '([lr])ves$', NULL), @@ -352,11 +363,12 @@ INSERT INTO inflection.inflection_rules ( ('plural', '(o)es$', NULL), ('plural', '(shoe)s$', NULL), ('plural', '(cris|ax|test)es$', NULL), - ('plural', '(octop|vir)i$', NULL), + ('plural', '(octop|vir)uses$', NULL), ('plural', '(alias|canvas|status|campus)es$', NULL), - ('plural', '^(summons)es$', NULL), + ('plural', '^(summons|bonus)es$', NULL), ('plural', '^(ox)en', NULL), ('plural', '(matr)ices$', NULL), + ('plural', '(vert|ind)ices$', NULL), ('plural', '^feet$', NULL), ('plural', '^teeth$', NULL), ('plural', '^geese$', NULL), @@ -364,19 +376,22 @@ INSERT INTO inflection.inflection_rules ( ('plural', '^(whereas)es$', NULL), ('plural', '^(criteri)a$', NULL), ('plural', '^genera$', NULL), + -- plural replacement rules ('plural', '^(m|wom)an$', E'\\1en'), ('plural', '(pe)rson$', E'\\1ople'), ('plural', '(child)$', E'\\1ren'), + ('plural', '(drive)$', E'\\1s'), ('plural', '^(ox)$', E'\\1en'), ('plural', '(ax|test)is$', E'\\1es'), - ('plural', '(octop|vir)us$', E'\\1i'), + ('plural', '(octop|vir)us$', E'\\1uses'), ('plural', '(alias|status|canvas|campus)$', E'\\1es'), - ('plural', '^(summons)$', E'\\1es'), + ('plural', '^(summons|bonus)$', E'\\1es'), ('plural', '(bu)s$', E'\\1ses'), ('plural', '(buffal|tomat|potat)o$', E'\\1oes'), ('plural', '([ti])um$', E'\\1a'), ('plural', 'sis$', 'ses'), ('plural', '(?:([^f])fe|([lr])f)$', E'\\1\\2ves'), + ('plural', '^(focus)$', E'\\1es'), ('plural', '(hi|ti)ve$', E'\\1ves'), ('plural', '([^aeiouy]|qu)y$', E'\\1ies'), ('plural', '(matr)ix$', E'\\1ices'), @@ -392,23 +407,27 @@ INSERT INTO inflection.inflection_rules ( ('plural', '^genus$', 'genera'), ('plural', 's$', 's'), ('plural', '$', 's'), + -- singular guards: already-singular words return as-is (NULL replacement) ('singular', '^(m|wom)an$', NULL), ('singular', '(pe)rson$', NULL), ('singular', '(child)$', NULL), + ('singular', '(drive)$', NULL), ('singular', '^(ox)$', NULL), ('singular', '(ax|test)is$', NULL), ('singular', '(octop|vir)us$', NULL), ('singular', '(alias|status|canvas|campus)$', NULL), - ('singular', '^(summons)$', NULL), + ('singular', '^(summons|bonus)$', NULL), ('singular', '(bu)s$', NULL), ('singular', '(buffal|tomat|potat)o$', NULL), ('singular', '([ti])um$', NULL), ('singular', 'sis$', NULL), ('singular', '(?:([^f])fe|([lr])f)$', NULL), + ('singular', '^(focus)$', NULL), ('singular', '(hi|ti)ve$', NULL), ('singular', '([^aeiouy]|qu)y$', NULL), ('singular', '(x|ch|ss|sh)$', NULL), ('singular', '(matr)ix$', NULL), + ('singular', '(vert|ind)ex$', NULL), ('singular', '([m|l])ouse$', NULL), ('singular', '^foot$', NULL), ('singular', '^tooth$', NULL), @@ -417,11 +436,19 @@ INSERT INTO inflection.inflection_rules ( ('singular', '^(whereas)$', NULL), ('singular', '^(criteri)on$', NULL), ('singular', '^genus$', NULL), + -- singular replacement rules ('singular', '^(m|wom)en$', E'\\1an'), ('singular', '(pe)ople$', E'\\1rson'), ('singular', '(child)ren$', E'\\1'), + ('singular', '(database)s$', E'\\1'), + ('singular', '(drive)s$', E'\\1'), ('singular', '^genera$', 'genus'), ('singular', '^(criteri)a$', E'\\1on'), + -- Latin suffix overrides (PostGraphile-compatible) + ('singular', '(schema)ta$', E'\\1'), + ('singular', '(phenomen)a$', E'\\1on'), + ('singular', '(memorand)a$', E'\\1um'), + ('singular', '(curricul)a$', E'\\1um'), ('singular', '([ti])a$', E'\\1um'), ('singular', '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$', E'\\1\\2sis'), ('singular', '(hi|ti)ves$', E'\\1ve'), @@ -438,9 +465,9 @@ INSERT INTO inflection.inflection_rules ( ('singular', '(o)es$', E'\\1'), ('singular', '(shoe)s$', E'\\1'), ('singular', '(cris|ax|test)es$', E'\\1is'), - ('singular', '(octop|vir)i$', E'\\1us'), + ('singular', '(octop|vir)uses$', E'\\1us'), ('singular', '(alias|canvas|status|campus)es$', E'\\1'), - ('singular', '^(summons)es$', E'\\1'), + ('singular', '^(summons|bonus)es$', E'\\1'), ('singular', '^(ox)en', E'\\1'), ('singular', '(matr)ices$', E'\\1ix'), ('singular', '(vert|ind)ices$', E'\\1ex'), @@ -452,4 +479,4 @@ INSERT INTO inflection.inflection_rules ( ('singular', 'ss$', 'ss'), ('singular', 's$', ''); -CREATE INDEX inflection_rules_type_idx ON inflection.inflection_rules (type); \ No newline at end of file +CREATE INDEX inflection_rules_type_idx ON inflection.inflection_rules (type); diff --git a/packages/jobs/deploy/schemas/app_jobs/tables/job_queues/table.sql b/packages/jobs/deploy/schemas/app_jobs/tables/job_queues/table.sql index 4ad1305c9..dd003c348 100644 --- a/packages/jobs/deploy/schemas/app_jobs/tables/job_queues/table.sql +++ b/packages/jobs/deploy/schemas/app_jobs/tables/job_queues/table.sql @@ -8,5 +8,12 @@ CREATE TABLE app_jobs.job_queues ( locked_at timestamptz, locked_by text ); + +COMMENT ON TABLE app_jobs.job_queues IS 'Queue metadata: tracks job counts and locking state for each named queue'; +COMMENT ON COLUMN app_jobs.job_queues.queue_name IS 'Unique name identifying this queue'; +COMMENT ON COLUMN app_jobs.job_queues.job_count IS 'Number of pending jobs in this queue'; +COMMENT ON COLUMN app_jobs.job_queues.locked_at IS 'Timestamp when this queue was locked for batch processing'; +COMMENT ON COLUMN app_jobs.job_queues.locked_by IS 'Identifier of the worker that currently holds the queue lock'; + COMMIT; diff --git a/packages/jobs/deploy/schemas/app_jobs/tables/jobs/table.sql b/packages/jobs/deploy/schemas/app_jobs/tables/jobs/table.sql index b692c2c57..667ae3a52 100644 --- a/packages/jobs/deploy/schemas/app_jobs/tables/jobs/table.sql +++ b/packages/jobs/deploy/schemas/app_jobs/tables/jobs/table.sql @@ -22,5 +22,20 @@ CREATE TABLE app_jobs.jobs ( CHECK (length(locked_by) > 3), UNIQUE (key) ); + +COMMENT ON TABLE app_jobs.jobs IS 'Background job queue: each row is a pending or in-progress task to be executed by a worker'; +COMMENT ON COLUMN app_jobs.jobs.id IS 'Auto-incrementing job identifier'; +COMMENT ON COLUMN app_jobs.jobs.queue_name IS 'Name of the queue this job belongs to; used for worker routing and concurrency control'; +COMMENT ON COLUMN app_jobs.jobs.task_identifier IS 'Identifier for the task type (maps to a worker handler function)'; +COMMENT ON COLUMN app_jobs.jobs.payload IS 'JSON payload of arguments passed to the task handler'; +COMMENT ON COLUMN app_jobs.jobs.priority IS 'Execution priority; lower numbers run first (default 0)'; +COMMENT ON COLUMN app_jobs.jobs.run_at IS 'Earliest time this job should be executed; used for delayed/scheduled execution'; +COMMENT ON COLUMN app_jobs.jobs.attempts IS 'Number of times this job has been attempted so far'; +COMMENT ON COLUMN app_jobs.jobs.max_attempts IS 'Maximum retry attempts before the job is considered permanently failed'; +COMMENT ON COLUMN app_jobs.jobs.key IS 'Optional unique deduplication key; prevents duplicate jobs with the same key'; +COMMENT ON COLUMN app_jobs.jobs.last_error IS 'Error message from the most recent failed attempt'; +COMMENT ON COLUMN app_jobs.jobs.locked_at IS 'Timestamp when a worker locked this job for processing'; +COMMENT ON COLUMN app_jobs.jobs.locked_by IS 'Identifier of the worker that currently holds the lock'; + COMMIT; diff --git a/packages/jobs/deploy/schemas/app_jobs/tables/scheduled_jobs/table.sql b/packages/jobs/deploy/schemas/app_jobs/tables/scheduled_jobs/table.sql index 58d024189..c2ecc435b 100644 --- a/packages/jobs/deploy/schemas/app_jobs/tables/scheduled_jobs/table.sql +++ b/packages/jobs/deploy/schemas/app_jobs/tables/scheduled_jobs/table.sql @@ -22,5 +22,20 @@ CREATE TABLE app_jobs.scheduled_jobs ( CHECK (length(locked_by) > 3), UNIQUE (key) ); + +COMMENT ON TABLE app_jobs.scheduled_jobs IS 'Recurring/cron-style job definitions: each row spawns jobs on a schedule defined by schedule_info'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.id IS 'Auto-incrementing scheduled job identifier'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.queue_name IS 'Name of the queue spawned jobs are placed into'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.task_identifier IS 'Task type identifier for spawned jobs'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.payload IS 'JSON payload passed to each spawned job'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.priority IS 'Priority assigned to spawned jobs (lower = higher priority)'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.max_attempts IS 'Max retry attempts for spawned jobs'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.key IS 'Optional unique deduplication key'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.locked_at IS 'Timestamp when the scheduler locked this record for processing'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.locked_by IS 'Identifier of the scheduler worker holding the lock'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.schedule_info IS 'JSON schedule configuration (e.g. cron expression, interval)'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.last_scheduled IS 'Timestamp when a job was last spawned from this schedule'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.last_scheduled_id IS 'ID of the last job spawned from this schedule'; + COMMIT; diff --git a/packages/jobs/sql/pgpm-jobs--0.15.3.sql b/packages/jobs/sql/pgpm-jobs--0.15.3.sql index 653cc1178..4c287f144 100644 --- a/packages/jobs/sql/pgpm-jobs--0.15.3.sql +++ b/packages/jobs/sql/pgpm-jobs--0.15.3.sql @@ -133,6 +133,20 @@ CREATE TABLE app_jobs.scheduled_jobs ( UNIQUE (key) ); +COMMENT ON TABLE app_jobs.scheduled_jobs IS 'Recurring/cron-style job definitions: each row spawns jobs on a schedule defined by schedule_info'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.id IS 'Auto-incrementing scheduled job identifier'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.queue_name IS 'Name of the queue spawned jobs are placed into'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.task_identifier IS 'Task type identifier for spawned jobs'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.payload IS 'JSON payload passed to each spawned job'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.priority IS 'Priority assigned to spawned jobs (lower = higher priority)'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.max_attempts IS 'Max retry attempts for spawned jobs'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.key IS 'Optional unique deduplication key'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.locked_at IS 'Timestamp when the scheduler locked this record for processing'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.locked_by IS 'Identifier of the scheduler worker holding the lock'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.schedule_info IS 'JSON schedule configuration (e.g. cron expression, interval)'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.last_scheduled IS 'Timestamp when a job was last spawned from this schedule'; +COMMENT ON COLUMN app_jobs.scheduled_jobs.last_scheduled_id IS 'ID of the last job spawned from this schedule'; + CREATE FUNCTION app_jobs.do_notify() RETURNS trigger AS $EOFCODE$ BEGIN PERFORM @@ -174,6 +188,20 @@ CREATE TABLE app_jobs.jobs ( UNIQUE (key) ); +COMMENT ON TABLE app_jobs.jobs IS 'Background job queue: each row is a pending or in-progress task to be executed by a worker'; +COMMENT ON COLUMN app_jobs.jobs.id IS 'Auto-incrementing job identifier'; +COMMENT ON COLUMN app_jobs.jobs.queue_name IS 'Name of the queue this job belongs to; used for worker routing and concurrency control'; +COMMENT ON COLUMN app_jobs.jobs.task_identifier IS 'Identifier for the task type (maps to a worker handler function)'; +COMMENT ON COLUMN app_jobs.jobs.payload IS 'JSON payload of arguments passed to the task handler'; +COMMENT ON COLUMN app_jobs.jobs.priority IS 'Execution priority; lower numbers run first (default 0)'; +COMMENT ON COLUMN app_jobs.jobs.run_at IS 'Earliest time this job should be executed; used for delayed/scheduled execution'; +COMMENT ON COLUMN app_jobs.jobs.attempts IS 'Number of times this job has been attempted so far'; +COMMENT ON COLUMN app_jobs.jobs.max_attempts IS 'Maximum retry attempts before the job is considered permanently failed'; +COMMENT ON COLUMN app_jobs.jobs.key IS 'Optional unique deduplication key; prevents duplicate jobs with the same key'; +COMMENT ON COLUMN app_jobs.jobs.last_error IS 'Error message from the most recent failed attempt'; +COMMENT ON COLUMN app_jobs.jobs.locked_at IS 'Timestamp when a worker locked this job for processing'; +COMMENT ON COLUMN app_jobs.jobs.locked_by IS 'Identifier of the worker that currently holds the lock'; + ALTER TABLE app_jobs.jobs ADD COLUMN created_at timestamptz; @@ -273,6 +301,12 @@ CREATE TABLE app_jobs.job_queues ( locked_by text ); +COMMENT ON TABLE app_jobs.job_queues IS 'Queue metadata: tracks job counts and locking state for each named queue'; +COMMENT ON COLUMN app_jobs.job_queues.queue_name IS 'Unique name identifying this queue'; +COMMENT ON COLUMN app_jobs.job_queues.job_count IS 'Number of pending jobs in this queue'; +COMMENT ON COLUMN app_jobs.job_queues.locked_at IS 'Timestamp when this queue was locked for batch processing'; +COMMENT ON COLUMN app_jobs.job_queues.locked_by IS 'Identifier of the worker that currently holds the queue lock'; + CREATE INDEX job_queues_locked_by_idx ON app_jobs.job_queues (locked_by); GRANT SELECT, INSERT, UPDATE, DELETE ON app_jobs.job_queues TO administrator; @@ -655,4 +689,4 @@ BEGIN * INTO v_job; RETURN v_job; END; -$EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; \ No newline at end of file +$EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER; diff --git a/packages/jwt-claims/Makefile b/packages/jwt-claims/Makefile index e51a5ff3f..e3f7bef25 100644 --- a/packages/jwt-claims/Makefile +++ b/packages/jwt-claims/Makefile @@ -1,5 +1,5 @@ EXTENSION = pgpm-jwt-claims -DATA = sql/pgpm-jwt-claims--0.15.3.sql +DATA = sql/pgpm-jwt-claims--0.15.5.sql PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/packages/jwt-claims/deploy/schemas/jwt_private/procedures/current_session_id.sql b/packages/jwt-claims/deploy/schemas/jwt_private/procedures/current_session_id.sql new file mode 100644 index 000000000..ccf41ae0a --- /dev/null +++ b/packages/jwt-claims/deploy/schemas/jwt_private/procedures/current_session_id.sql @@ -0,0 +1,18 @@ +-- Deploy schemas/jwt_private/procedures/current_session_id to pg +-- Retrieves the current session ID from JWT claims (private/internal use) + +-- requires: schemas/jwt_private/schema + +BEGIN; + +-- Returns the current session UUID from the JWT claims +-- Used for session tracking, revocation, and audit logging +-- This is kept private to prevent session IDs from being exposed to the frontend +CREATE FUNCTION jwt_private.current_session_id() + RETURNS uuid +AS $$ + SELECT nullif(current_setting('jwt.claims.session_id', true), '')::uuid; +$$ +LANGUAGE 'sql' STABLE; + +COMMIT; diff --git a/packages/jwt-claims/pgpm-jwt-claims.control b/packages/jwt-claims/pgpm-jwt-claims.control index 3e3b2e2df..ebf219343 100644 --- a/packages/jwt-claims/pgpm-jwt-claims.control +++ b/packages/jwt-claims/pgpm-jwt-claims.control @@ -1,6 +1,6 @@ # pgpm-jwt-claims extension comment = 'pgpm-jwt-claims extension' -default_version = '0.15.3' +default_version = '0.15.5' module_pathname = '$libdir/pgpm-jwt-claims' requires = 'plpgsql,uuid-ossp,pgpm-types,pgpm-verify' relocatable = false diff --git a/packages/jwt-claims/pgpm.plan b/packages/jwt-claims/pgpm.plan index a95cc5f0e..e24acdc67 100644 --- a/packages/jwt-claims/pgpm.plan +++ b/packages/jwt-claims/pgpm.plan @@ -16,3 +16,4 @@ schemas/jwt_public/procedures/current_origin [schemas/jwt_public/schema] 2017-08 schemas/jwt_private/schema 2020-12-17T06:47:34Z Dan Lynch # add schemas/jwt_private/schema schemas/jwt_private/procedures/current_database_id [schemas/jwt_private/schema] 2020-12-17T23:22:28Z Dan Lynch # add schemas/jwt_private/procedures/current_database_id schemas/jwt_private/procedures/current_token_id [schemas/jwt_private/schema] 2017-08-11T08:11:51Z skitch # add schemas/jwt_private/procedures/current_token_id +schemas/jwt_private/procedures/current_session_id [schemas/jwt_private/schema] 2026-01-28T05:44:00Z Dan Lynch # add schemas/jwt_private/procedures/current_session_id diff --git a/packages/jwt-claims/revert/schemas/jwt_private/procedures/current_session_id.sql b/packages/jwt-claims/revert/schemas/jwt_private/procedures/current_session_id.sql new file mode 100644 index 000000000..fb07278e0 --- /dev/null +++ b/packages/jwt-claims/revert/schemas/jwt_private/procedures/current_session_id.sql @@ -0,0 +1,7 @@ +-- Revert schemas/jwt_private/procedures/current_session_id from pg + +BEGIN; + +DROP FUNCTION jwt_private.current_session_id; + +COMMIT; diff --git a/packages/jwt-claims/sql/pgpm-jwt-claims--0.15.5.sql b/packages/jwt-claims/sql/pgpm-jwt-claims--0.15.5.sql new file mode 100644 index 000000000..e2f1afb7e --- /dev/null +++ b/packages/jwt-claims/sql/pgpm-jwt-claims--0.15.5.sql @@ -0,0 +1,147 @@ +\echo Use "CREATE EXTENSION pgpm-jwt-claims" to load this file. \quit +CREATE SCHEMA ctx; + +GRANT USAGE ON SCHEMA ctx TO authenticated, anonymous; + +ALTER DEFAULT PRIVILEGES IN SCHEMA ctx + GRANT EXECUTE ON FUNCTIONS TO authenticated; + +CREATE FUNCTION ctx.ip_address() RETURNS inet AS $EOFCODE$ + SELECT nullif(current_setting('jwt.claims.ip_address', true), '')::inet; +$EOFCODE$ LANGUAGE sql STABLE; + +CREATE FUNCTION ctx.origin() RETURNS origin AS $EOFCODE$ + SELECT nullif(current_setting('jwt.claims.origin', true), '')::origin; +$EOFCODE$ LANGUAGE sql STABLE; + +CREATE FUNCTION ctx.uagent() RETURNS text AS $EOFCODE$ + SELECT nullif(current_setting('jwt.claims.user_agent', true), ''); +$EOFCODE$ LANGUAGE sql STABLE; + +CREATE FUNCTION ctx.uid() RETURNS uuid AS $EOFCODE$ + SELECT nullif(current_setting('jwt.claims.user_id', true), '')::uuid; +$EOFCODE$ LANGUAGE sql STABLE; + +DO $EOFCODE$ + DECLARE + BEGIN + EXECUTE format('CREATE FUNCTION ctx.security_definer() returns text as $FUNC$ + SELECT ''%s''; +$FUNC$ +LANGUAGE ''sql'';', current_user); + EXECUTE format('CREATE FUNCTION ctx.is_security_definer() returns bool as $FUNC$ + SELECT ''%s'' = current_user; +$FUNC$ +LANGUAGE ''sql'';', current_user); + END; +$EOFCODE$; + +GRANT EXECUTE ON FUNCTION ctx.security_definer() TO PUBLIC; + +GRANT EXECUTE ON FUNCTION ctx.is_security_definer() TO PUBLIC; + +CREATE SCHEMA jwt_public; + +GRANT USAGE ON SCHEMA jwt_public TO authenticated, anonymous; + +ALTER DEFAULT PRIVILEGES IN SCHEMA jwt_public + GRANT EXECUTE ON FUNCTIONS TO authenticated; + +CREATE FUNCTION jwt_public.current_user_id() RETURNS uuid AS $EOFCODE$ +DECLARE + v_identifier_id uuid; +BEGIN + IF current_setting('jwt.claims.user_id', TRUE) + IS NOT NULL THEN + BEGIN + v_identifier_id = current_setting('jwt.claims.user_id', TRUE)::uuid; + EXCEPTION + WHEN OTHERS THEN + RAISE NOTICE 'Invalid UUID value'; + RETURN NULL; + END; + RETURN v_identifier_id; + ELSE + RETURN NULL; + END IF; +END; +$EOFCODE$ LANGUAGE plpgsql STABLE; + +CREATE FUNCTION jwt_public.current_ip_address() RETURNS inet AS $EOFCODE$ +DECLARE + v_ip_addr inet; +BEGIN + IF current_setting('jwt.claims.ip_address', TRUE) + IS NOT NULL THEN + BEGIN + v_ip_addr = trim(current_setting('jwt.claims.ip_address', TRUE))::inet; + EXCEPTION + WHEN OTHERS THEN + RAISE NOTICE 'Invalid IP'; + RETURN NULL; + END; + RETURN v_ip_addr; + ELSE + RETURN NULL; + END IF; +END; +$EOFCODE$ LANGUAGE plpgsql STABLE; + +CREATE FUNCTION jwt_public.current_user_agent() RETURNS text AS $EOFCODE$ +DECLARE + v_uagent text; +BEGIN + IF current_setting('jwt.claims.user_agent', TRUE) + IS NOT NULL THEN + BEGIN + v_uagent = current_setting('jwt.claims.user_agent', TRUE); + EXCEPTION + WHEN OTHERS THEN + RAISE NOTICE 'Invalid UserAgent'; + RETURN NULL; + END; + RETURN v_uagent; + ELSE + RETURN NULL; + END IF; +END; +$EOFCODE$ LANGUAGE plpgsql STABLE; + +CREATE FUNCTION jwt_public.current_origin() RETURNS origin AS $EOFCODE$ + SELECT nullif(current_setting('jwt.claims.origin', true), '')::origin; +$EOFCODE$ LANGUAGE sql STABLE; + +CREATE SCHEMA jwt_private; + +GRANT USAGE ON SCHEMA jwt_private TO authenticated, anonymous; + +ALTER DEFAULT PRIVILEGES IN SCHEMA jwt_private + GRANT EXECUTE ON FUNCTIONS TO authenticated; + +CREATE FUNCTION jwt_private.current_database_id() RETURNS uuid AS $EOFCODE$ +DECLARE + v_identifier_id uuid; +BEGIN + IF current_setting('jwt.claims.database_id', TRUE) + IS NOT NULL THEN + BEGIN + v_identifier_id = current_setting('jwt.claims.database_id', TRUE)::uuid; + EXCEPTION + WHEN OTHERS THEN + RAISE NOTICE 'Invalid UUID value'; + RETURN NULL; + END; + RETURN v_identifier_id; + ELSE + RETURN NULL; + END IF; +END; +$EOFCODE$ LANGUAGE plpgsql STABLE; + +CREATE FUNCTION jwt_private.current_token_id() RETURNS uuid AS $EOFCODE$ + SELECT nullif(current_setting('jwt.claims.token_id', true), '')::uuid; +$EOFCODE$ LANGUAGE sql STABLE; + +CREATE FUNCTION jwt_private.current_session_id() RETURNS uuid AS $EOFCODE$ + SELECT nullif(current_setting('jwt.claims.session_id', true), '')::uuid; +$EOFCODE$ LANGUAGE sql STABLE; diff --git a/packages/jwt-claims/verify/schemas/jwt_private/procedures/current_session_id.sql b/packages/jwt-claims/verify/schemas/jwt_private/procedures/current_session_id.sql new file mode 100644 index 000000000..8eebd8340 --- /dev/null +++ b/packages/jwt-claims/verify/schemas/jwt_private/procedures/current_session_id.sql @@ -0,0 +1,7 @@ +-- Verify schemas/jwt_private/procedures/current_session_id on pg + +BEGIN; + +SELECT verify_function ('jwt_private.current_session_id'); + +ROLLBACK; diff --git a/packages/measurements/deploy/schemas/measurements/tables/quantities/table.sql b/packages/measurements/deploy/schemas/measurements/tables/quantities/table.sql index 0a6a65a4e..3988d89ce 100644 --- a/packages/measurements/deploy/schemas/measurements/tables/quantities/table.sql +++ b/packages/measurements/deploy/schemas/measurements/tables/quantities/table.sql @@ -13,4 +13,12 @@ CREATE TABLE measurements.quantities ( description text ); +COMMENT ON TABLE measurements.quantities IS 'Unit of measure definitions: maps quantity names to their display labels, units, and descriptions'; +COMMENT ON COLUMN measurements.quantities.id IS 'Auto-incrementing identifier for this quantity'; +COMMENT ON COLUMN measurements.quantities.name IS 'Machine-readable name for this quantity (e.g. length, mass, temperature)'; +COMMENT ON COLUMN measurements.quantities.label IS 'Human-readable display label'; +COMMENT ON COLUMN measurements.quantities.unit IS 'Unit symbol or abbreviation (e.g. m, kg, °C)'; +COMMENT ON COLUMN measurements.quantities.unit_desc IS 'Full unit name (e.g. meters, kilograms, degrees Celsius)'; +COMMENT ON COLUMN measurements.quantities.description IS 'Detailed description of what this quantity measures'; + COMMIT; diff --git a/packages/measurements/sql/pgpm-measurements--0.15.3.sql b/packages/measurements/sql/pgpm-measurements--0.15.3.sql index 0dc786469..b9306df74 100644 --- a/packages/measurements/sql/pgpm-measurements--0.15.3.sql +++ b/packages/measurements/sql/pgpm-measurements--0.15.3.sql @@ -10,6 +10,14 @@ CREATE TABLE measurements.quantities ( description text ); +COMMENT ON TABLE measurements.quantities IS 'Unit of measure definitions: maps quantity names to their display labels, units, and descriptions'; +COMMENT ON COLUMN measurements.quantities.id IS 'Auto-incrementing identifier for this quantity'; +COMMENT ON COLUMN measurements.quantities.name IS 'Machine-readable name for this quantity (e.g. length, mass, temperature)'; +COMMENT ON COLUMN measurements.quantities.label IS 'Human-readable display label'; +COMMENT ON COLUMN measurements.quantities.unit IS 'Unit symbol or abbreviation (e.g. m, kg, °C)'; +COMMENT ON COLUMN measurements.quantities.unit_desc IS 'Full unit name (e.g. meters, kilograms, degrees Celsius)'; +COMMENT ON COLUMN measurements.quantities.description IS 'Detailed description of what this quantity measures'; + INSERT INTO measurements.quantities ( id, name, @@ -73,4 +81,4 @@ INSERT INTO measurements.quantities ( ) VALUES (45, 'Percent', 'Percent', '%', 'percentage', 'a number or ratio expressed as a fraction of 100'), (46, 'PartsPerMillion', 'Parts per Million', 'ppm', 'parts per million', 'pseudo-units to describe small values of miscellaneous dimensionless quantities that are pure numbers representing a quantity-per-quantity measure in parts per million'), - (47, 'PartsPerBillion', 'Parts per Billion', 'ppb', 'parts per billion', 'pseudo-units to describe small values of miscellaneous dimensionless quantities that are pure numbers representing a quantity-per-quantity measure in parts per billion'); \ No newline at end of file + (47, 'PartsPerBillion', 'Parts per Billion', 'ppb', 'parts per billion', 'pseudo-units to describe small values of miscellaneous dimensionless quantities that are pure numbers representing a quantity-per-quantity measure in parts per billion'); diff --git a/packages/metaschema-modules/README.md b/packages/metaschema-modules/README.md index b80d42f60..27e2c8226 100644 --- a/packages/metaschema-modules/README.md +++ b/packages/metaschema-modules/README.md @@ -1,4 +1,4 @@ -# @pgpm/metaschema-modules +# @pgpm/db-meta-modules

@@ -9,14 +9,14 @@ - +

Module metadata handling and dependency tracking. ## Overview -`@pgpm/metaschema-modules` extends the `@pgpm/metaschema-schema` package with module-specific metadata tables. This package provides tables for tracking various pgpm modules including authentication, permissions, memberships, encrypted secrets, and more. It enables configuration and metadata storage for modular application features. +`@pgpm/db-meta-modules` extends the `@pgpm/db-meta-schema` package with module-specific metadata tables. This package provides tables for tracking various pgpm modules including authentication, permissions, memberships, encrypted secrets, and more. It enables configuration and metadata storage for modular application features. ## Features @@ -33,7 +33,7 @@ Module metadata handling and dependency tracking. If you have `pgpm` installed: ```bash -pgpm install @pgpm/metaschema-modules +pgpm install @pgpm/db-meta-modules pgpm deploy ``` @@ -56,7 +56,7 @@ eval "$(pgpm env)" ```bash # 1. Install the package -pgpm install @pgpm/metaschema-modules +pgpm install @pgpm/db-meta-modules # 2. Deploy locally pgpm deploy @@ -74,7 +74,7 @@ pgpm init # 3. Install a package cd packages/my-module -pgpm install @pgpm/metaschema-modules +pgpm install @pgpm/db-meta-modules # 4. Deploy everything pgpm deploy --createdb --database mydb1 @@ -213,8 +213,7 @@ Use module tables as feature flags: ## Dependencies -- `@pgpm/metaschema-schema`: Core metadata management -- `@pgpm/services`: Services schemas for APIs, sites, and domains +- `@pgpm/db-meta-schema`: Core metadata management - `@pgpm/verify`: Verification utilities ## Testing diff --git a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/field_module/table.sql b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/field_module/table.sql index dd6c431d8..afb49ad06 100644 --- a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/field_module/table.sql +++ b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/field_module/table.sql @@ -13,8 +13,14 @@ CREATE TABLE metaschema_modules_public.field_module ( table_id uuid NOT NULL DEFAULT uuid_nil(), field_id uuid NOT NULL DEFAULT uuid_nil(), + -- Node type from node_type_registry (e.g., 'FieldSlug', 'FieldImmutable', 'FieldInflection', 'FieldOwned') node_type text NOT NULL, + -- Type-specific parameters as jsonb + -- FieldSlug: {"source_field_id": "uuid"} + -- FieldImmutable: {} (no extra params) + -- FieldInflection: {"ops": ["snake_case", "uppercase"]} + -- FieldOwned: {"role_key_field_id": "uuid", "protected_field_ids": ["uuid", ...]} data jsonb NOT NULL DEFAULT '{}', triggers text[], @@ -35,4 +41,3 @@ CREATE INDEX field_module_database_id_idx ON metaschema_modules_public.field_mod CREATE INDEX field_module_node_type_idx ON metaschema_modules_public.field_module ( node_type ); COMMIT; - diff --git a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/table_template_module/table.sql b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/table_template_module/table.sql index 06deb600d..dde40ceab 100644 --- a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/table_template_module/table.sql +++ b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/table_template_module/table.sql @@ -16,8 +16,13 @@ CREATE TABLE metaschema_modules_public.table_template_module ( table_name text NOT NULL, + -- Node type from node_type_registry (e.g., 'TableUserProfiles', 'TableOrganizationSettings', 'TableUserSettings') node_type text NOT NULL, + -- Type-specific parameters as jsonb + -- TableUserProfiles: {} (uses default fields) + -- TableOrganizationSettings: {} (uses default fields) + -- TableUserSettings: {} (uses default fields) data jsonb NOT NULL DEFAULT '{}', -- diff --git a/packages/metaschema-schema/README.md b/packages/metaschema-schema/README.md index 5a5e9c14f..854a5a63f 100644 --- a/packages/metaschema-schema/README.md +++ b/packages/metaschema-schema/README.md @@ -1,4 +1,4 @@ -# @pgpm/metaschema-schema +# @pgpm/db-meta-schema

@@ -9,14 +9,14 @@ - +

Database metadata utilities and introspection functions. ## Overview -`@pgpm/metaschema-schema` provides a comprehensive metadata management system for PostgreSQL databases. This package creates tables and schemas for storing and querying database structure information including databases, schemas, tables, fields, constraints, indexes, and more. It enables runtime schema introspection, metadata-driven code generation, and database structure management. +`@pgpm/db-meta-schema` provides a comprehensive metadata management system for PostgreSQL databases. This package creates tables and schemas for storing and querying database structure information including databases, schemas, tables, fields, constraints, indexes, and more. It enables runtime schema introspection, metadata-driven code generation, and database structure management. ## Features @@ -25,6 +25,7 @@ Database metadata utilities and introspection functions. - **Index Management**: Store and query index definitions - **Trigger and Procedure Metadata**: Track database functions and triggers - **RLS and Policy Information**: Store row-level security policies +- **Extension Tracking**: Manage database extensions and their relationships - **API and Site Metadata**: Store API configurations and site information - **GraphQL Integration**: Smart tags and annotations for GraphQL schema generation @@ -33,7 +34,7 @@ Database metadata utilities and introspection functions. If you have `pgpm` installed: ```bash -pgpm install @pgpm/metaschema-schema +pgpm install @pgpm/db-meta-schema pgpm deploy ``` @@ -56,7 +57,7 @@ eval "$(pgpm env)" ```bash # 1. Install the package -pgpm install @pgpm/metaschema-schema +pgpm install @pgpm/db-meta-schema # 2. Deploy locally pgpm deploy @@ -74,7 +75,7 @@ pgpm init # 3. Install a package cd packages/my-module -pgpm install @pgpm/metaschema-schema +pgpm install @pgpm/db-meta-schema # 4. Deploy everything pgpm deploy --createdb --database mydb1 @@ -98,6 +99,8 @@ Stores database structure metadata: - **trigger**: Trigger definitions - **procedure**: Stored procedure definitions - **policy**: Row-level security policies +- **extension**: PostgreSQL extensions +- **database_extension**: Extension installations per database ### metaschema_private Schema @@ -108,6 +111,7 @@ Private schema for internal metadata operations. Application-level metadata: - **apis**: API configurations +- **api_extensions**: API extension relationships - **api_modules**: API module definitions - **api_schemas**: API schema configurations - **sites**: Site definitions diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/database/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/database/table.sql index fca5045a4..c9299c373 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/database/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/database/table.sql @@ -8,23 +8,17 @@ CREATE TABLE metaschema_public.database ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), owner_id uuid, schema_hash text, - schema_name text, - private_schema_name text, name text, label text, hash uuid, - unique(schema_hash), - unique(schema_name), - unique(private_schema_name) + unique(schema_hash) ); ALTER TABLE metaschema_public.database ADD CONSTRAINT db_namechk CHECK (char_length(name) > 2); COMMENT ON COLUMN metaschema_public.database.schema_hash IS '@omit'; --- COMMENT ON COLUMN metaschema_public.database.schema_name IS '@omit'; --- COMMENT ON COLUMN metaschema_public.database.private_schema_name IS '@omit'; COMMIT; diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/field/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/field/table.sql index e1851a39a..a2e08484d 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/field/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/field/table.sql @@ -3,10 +3,25 @@ -- requires: schemas/metaschema_public/schema -- requires: schemas/metaschema_public/tables/table/table --- requires: schemas/metaschema_public/types/object_category BEGIN; +-- TODO should we just query this table and make a view? +-- https://www.postgresql.org/docs/9.2/catalog-pg-attribute.html + +-- IF YOU WANT TO REMOVE THIS TABLE, answer the qustion, how would you add RLS to this: +-- SELECT +-- attrelid::text AS tbl +-- , attname::text AS col +-- , p.attnum::int as id, +-- t.typname as typename + +-- FROM pg_catalog.pg_attribute p +-- INNER JOIN pg_catalog.pg_type t ON (t.oid = p.atttypid) +-- WHERE attrelid = 'dude_schema.products'::regclass +-- AND p.attnum > 0 +-- AND NOT attisdropped; + CREATE TABLE metaschema_public.field ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), database_id uuid NOT NULL DEFAULT uuid_nil(), @@ -41,11 +56,14 @@ CREATE TABLE metaschema_public.field ( tags citext[] NOT NULL DEFAULT '{}', + -- Field categorization for system/module/app fields (mirrors table categorization) + -- category: 'core' for system fields (id, entity_id, actor_id), 'module' for module-generated fields, 'app' for user-defined fields + -- module: the module name that created this field (e.g., 'users', 'permissions', 'memberships') + -- scope: membership_type int (1=app, 2=org, 3=group, NULL=not scoped) category metaschema_public.object_category NOT NULL DEFAULT 'app', module text NULL, scope int NULL, - -- CONSTRAINT db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE, CONSTRAINT table_fkey FOREIGN KEY (table_id) REFERENCES metaschema_public.table (id) ON DELETE CASCADE, diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/foreign_key_constraint/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/foreign_key_constraint/table.sql index f6b0bc8e0..14d5a1e4c 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/foreign_key_constraint/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/foreign_key_constraint/table.sql @@ -19,7 +19,7 @@ CREATE TABLE metaschema_public.foreign_key_constraint ( field_ids uuid[] NOT NULL, ref_table_id uuid NOT NULL REFERENCES metaschema_public.table (id) ON DELETE CASCADE, ref_field_ids uuid[] NOT NULL, - delete_action char(1) DEFAULT 'c', + delete_action char(1) DEFAULT 'c', -- postgres default is 'a' update_action char(1) DEFAULT 'a', category metaschema_public.object_category NOT NULL DEFAULT 'app', diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/index/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/index/table.sql index f575950ca..320d44b1a 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/index/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/index/table.sql @@ -33,6 +33,7 @@ CREATE TABLE metaschema_public.index ( CONSTRAINT db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE, CONSTRAINT table_fkey FOREIGN KEY (table_id) REFERENCES metaschema_public.table (id) ON DELETE CASCADE, + -- index names are UNIQUE across schemas, so for portability we will check against database_id UNIQUE (database_id, name) ); diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/policy/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/policy/table.sql index e3ea79fcd..3a78cf476 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/policy/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/policy/table.sql @@ -15,6 +15,10 @@ CREATE TABLE metaschema_public.policy ( role_name text, privilege text, + -- using_expression text, + -- check_expression text, + -- policy_text text, + permissive boolean default true, disabled boolean default false, diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/procedure/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/procedure/table.sql index 701ff7b2a..d9eea8f78 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/procedure/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/procedure/table.sql @@ -12,6 +12,9 @@ CREATE TABLE metaschema_public.procedure ( name text NOT NULL, + -- MAYBE MAKE A SPECIAL RLS functions for policy making... + + -- can we make this all JSON? argnames text[], argtypes text[], argdefaults text[], diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/schema/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/schema/table.sql index cccf5d5e8..f22c7f0c6 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/schema/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/schema/table.sql @@ -31,6 +31,9 @@ CREATE TABLE metaschema_public.schema ( UNIQUE (schema_name) ); +-- TODO: build out services +-- COMMENT ON COLUMN metaschema_public.schema.schema_name IS '@omit'; + ALTER TABLE metaschema_public.schema ADD CONSTRAINT schema_namechk CHECK (char_length(name) > 2); diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/table/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/table/table.sql index e5d8bfdf0..71a75a07a 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/table/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/table/table.sql @@ -33,8 +33,6 @@ CREATE TABLE metaschema_public.table ( tags citext[] NOT NULL DEFAULT '{}', - -- - CONSTRAINT db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE, CONSTRAINT schema_fkey FOREIGN KEY (schema_id) REFERENCES metaschema_public.schema (id) ON DELETE CASCADE, diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/trigger/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/trigger/table.sql index 0258142a7..c38e79c0b 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/trigger/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/trigger/table.sql @@ -6,13 +6,15 @@ BEGIN; +-- https://www.postgresql.org/docs/12/sql-createtrigger.html + CREATE TABLE metaschema_public.trigger ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), database_id uuid NOT NULL DEFAULT uuid_nil(), table_id uuid NOT NULL, name text NOT NULL, - event text, + event text, -- INSERT, UPDATE, DELETE, or TRUNCATE function_name text, smart_tags jsonb, diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/unique_constraint/table.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/unique_constraint/table.sql index 6497989bb..ff46166aa 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/unique_constraint/table.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/tables/unique_constraint/table.sql @@ -27,6 +27,9 @@ CREATE TABLE metaschema_public.unique_constraint ( CONSTRAINT db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE, CONSTRAINT table_fkey FOREIGN KEY (table_id) REFERENCES metaschema_public.table (id) ON DELETE CASCADE, + -- TODO these are unique across schema, NOT table. We'll need to update this to have database_id + -- for portability + UNIQUE (table_id, name), CHECK (field_ids <> '{}') ); diff --git a/packages/metaschema-schema/deploy/schemas/metaschema_public/types/object_category.sql b/packages/metaschema-schema/deploy/schemas/metaschema_public/types/object_category.sql index ea77e21ec..9480d9f52 100644 --- a/packages/metaschema-schema/deploy/schemas/metaschema_public/types/object_category.sql +++ b/packages/metaschema-schema/deploy/schemas/metaschema_public/types/object_category.sql @@ -4,6 +4,10 @@ BEGIN; +-- Unified category type for all metaschema objects (tables, fields, procedures, triggers, indexes, policies, constraints, etc.) +-- 'core' - system-level objects (id fields, entity_id, actor_id, etc.) +-- 'module' - objects created by modules (users, permissions, memberships, etc.) +-- 'app' - user-defined application objects CREATE TYPE metaschema_public.object_category AS ENUM ('core', 'module', 'app'); COMMIT; diff --git a/packages/metaschema-schema/pgpm.plan b/packages/metaschema-schema/pgpm.plan index 41952e02a..947eb5b30 100644 --- a/packages/metaschema-schema/pgpm.plan +++ b/packages/metaschema-schema/pgpm.plan @@ -4,28 +4,27 @@ schemas/metaschema_private/schema [pgpm-inflection:schemas/inflection/tables/inflection_rules/indexes/inflection_rules_type_idx pgpm-database-jobs:schemas/app_jobs/triggers/tg_add_job_with_row pgpm-types:schemas/public/domains/url] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_private/schema schemas/metaschema_public/schema 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/schema -schemas/metaschema_public/types/object_category [schemas/metaschema_public/schema] 2026-01-14T00:00:00Z devin # add schemas/metaschema_public/types/object_category +schemas/metaschema_public/types/object_category [schemas/metaschema_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/types/object_category schemas/metaschema_public/tables/database/table [schemas/metaschema_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/database/table schemas/metaschema_public/tables/schema/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/schema/table schemas/metaschema_public/tables/table/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table schemas/metaschema_public/tables/schema/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/table/table -schemas/metaschema_public/tables/check_constraint/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table schemas/metaschema_public/tables/table/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/check_constraint/table +schemas/metaschema_public/tables/check_constraint/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table schemas/metaschema_public/tables/table/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/check_constraint/table schemas/metaschema_public/tables/database/indexes/databases_database_unique_name_idx [schemas/metaschema_private/schema schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/database/indexes/databases_database_unique_name_idx -schemas/metaschema_public/tables/field/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/field/table +schemas/metaschema_public/tables/field/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/field/table schemas/metaschema_public/tables/field/indexes/databases_field_uniq_names_idx [schemas/metaschema_public/schema schemas/metaschema_public/tables/field/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/field/indexes/databases_field_uniq_names_idx -schemas/metaschema_public/tables/foreign_key_constraint/table [schemas/metaschema_public/tables/field/table schemas/metaschema_public/tables/table/table schemas/metaschema_public/schema schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/foreign_key_constraint/table +schemas/metaschema_public/tables/foreign_key_constraint/table [schemas/metaschema_public/tables/field/table schemas/metaschema_public/tables/table/table schemas/metaschema_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/foreign_key_constraint/table schemas/metaschema_public/tables/full_text_search/table [schemas/metaschema_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/full_text_search/table -schemas/metaschema_public/tables/index/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table schemas/metaschema_public/tables/database/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/index/table +schemas/metaschema_public/tables/index/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table schemas/metaschema_public/tables/database/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/index/table schemas/metaschema_public/tables/limit_function/table [schemas/metaschema_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/limit_function/table -schemas/metaschema_public/tables/policy/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/policy/table -schemas/metaschema_public/tables/primary_key_constraint/table [schemas/metaschema_public/schema schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/primary_key_constraint/table -schemas/metaschema_public/tables/procedure/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/procedure/table -schemas/metaschema_public/tables/rls_function/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/rls_function/table +schemas/metaschema_public/tables/policy/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/policy/table +schemas/metaschema_public/tables/primary_key_constraint/table [schemas/metaschema_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/primary_key_constraint/table +schemas/metaschema_public/tables/procedure/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/procedure/table schemas/metaschema_public/tables/schema_grant/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/schema/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/schema_grant/table schemas/metaschema_public/tables/table_grant/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/table_grant/table schemas/metaschema_public/tables/table/indexes/databases_table_unique_name_idx [schemas/metaschema_public/schema schemas/metaschema_private/schema schemas/metaschema_public/tables/table/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/table/indexes/databases_table_unique_name_idx schemas/metaschema_public/tables/trigger_function/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/trigger_function/table -schemas/metaschema_public/tables/trigger/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/trigger/table -schemas/metaschema_public/tables/unique_constraint/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table schemas/metaschema_public/tables/table/table schemas/metaschema_public/types/object_category] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/unique_constraint/table +schemas/metaschema_public/tables/trigger/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/table/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/trigger/table +schemas/metaschema_public/tables/unique_constraint/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/database/table schemas/metaschema_public/tables/table/table] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_public/tables/unique_constraint/table schemas/metaschema_public/tables/view/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/schema/table schemas/metaschema_public/tables/table/table schemas/metaschema_public/tables/database/table schemas/metaschema_public/types/object_category] 2026-01-23T00:00:00Z devin # add schemas/metaschema_public/tables/view/table schemas/metaschema_public/tables/view_table/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/view/table schemas/metaschema_public/tables/table/table] 2026-01-23T00:00:00Z devin # add schemas/metaschema_public/tables/view_table/table schemas/metaschema_public/tables/view_grant/table [schemas/metaschema_public/schema schemas/metaschema_public/tables/view/table schemas/metaschema_public/tables/database/table] 2026-01-23T00:00:00Z devin # add schemas/metaschema_public/tables/view_grant/table diff --git a/packages/metaschema-schema/revert/schemas/metaschema_public/types/object_category.sql b/packages/metaschema-schema/revert/schemas/metaschema_public/types/object_category.sql index 84ba74ed2..68174dd43 100644 --- a/packages/metaschema-schema/revert/schemas/metaschema_public/types/object_category.sql +++ b/packages/metaschema-schema/revert/schemas/metaschema_public/types/object_category.sql @@ -2,6 +2,6 @@ BEGIN; -DROP TYPE IF EXISTS metaschema_public.object_category; +DROP TYPE metaschema_public.object_category; COMMIT; diff --git a/packages/metaschema-schema/sql/metaschema-schema--0.15.5.sql b/packages/metaschema-schema/sql/metaschema-schema--0.15.5.sql index ee9766734..97d86e491 100644 --- a/packages/metaschema-schema/sql/metaschema-schema--0.15.5.sql +++ b/packages/metaschema-schema/sql/metaschema-schema--0.15.5.sql @@ -25,18 +25,16 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA metaschema_public ALTER DEFAULT PRIVILEGES IN SCHEMA metaschema_public GRANT ALL ON FUNCTIONS TO authenticated; +CREATE TYPE metaschema_public.object_category AS ENUM ('core', 'module', 'app'); + CREATE TABLE metaschema_public.database ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), owner_id uuid, schema_hash text, - schema_name text, - private_schema_name text, name text, label text, hash uuid, - UNIQUE (schema_hash), - UNIQUE (schema_name), - UNIQUE (private_schema_name) + UNIQUE (schema_hash) ); ALTER TABLE metaschema_public.database @@ -52,6 +50,12 @@ CREATE TABLE metaschema_public.schema ( schema_name text NOT NULL, label text, description text, + smart_tags jsonb, + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', + is_public boolean NOT NULL DEFAULT true, CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) @@ -68,8 +72,6 @@ COMMENT ON CONSTRAINT db_fkey ON metaschema_public.schema IS '@omit manyToMany'; CREATE INDEX schema_database_id_idx ON metaschema_public.schema (database_id); -CREATE TYPE metaschema_public.table_category AS ENUM ('core', 'module', 'app'); - CREATE TABLE metaschema_public."table" ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), database_id uuid NOT NULL DEFAULT uuid_nil(), @@ -78,7 +80,7 @@ CREATE TABLE metaschema_public."table" ( label text, description text, smart_tags jsonb, - category metaschema_public.table_category NOT NULL DEFAULT 'app', + category metaschema_public.object_category NOT NULL DEFAULT 'app', module text NULL, scope int NULL, use_rls boolean NOT NULL DEFAULT false, @@ -119,6 +121,11 @@ CREATE TABLE metaschema_public.check_constraint ( type text, field_ids uuid[] NOT NULL, expr jsonb, + smart_tags jsonb, + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) @@ -146,8 +153,6 @@ $EOFCODE$ LANGUAGE sql IMMUTABLE; CREATE UNIQUE INDEX databases_database_unique_name_idx ON metaschema_public.database (owner_id, (metaschema_private.database_name_hash(name))); -CREATE TYPE metaschema_public.field_category AS ENUM ('core', 'module', 'app'); - CREATE TABLE metaschema_public.field ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), database_id uuid NOT NULL DEFAULT uuid_nil(), @@ -168,7 +173,7 @@ CREATE TABLE metaschema_public.field ( min double precision DEFAULT NULL, max double precision DEFAULT NULL, tags citext[] NOT NULL DEFAULT '{}', - category metaschema_public.field_category NOT NULL DEFAULT 'app', + category metaschema_public.object_category NOT NULL DEFAULT 'app', module text NULL, scope int NULL, CONSTRAINT db_fkey @@ -192,7 +197,10 @@ CREATE INDEX field_database_id_idx ON metaschema_public.field (database_id); COMMENT ON COLUMN metaschema_public.field.default_value IS '@sqlExpression'; -CREATE UNIQUE INDEX databases_field_uniq_names_idx ON metaschema_public.field (table_id, (decode(md5(lower(CASE WHEN type = 'uuid' THEN regexp_replace(name, '^(.+?)(_row_id|_id|_uuid|_fk|_pk)$', E'\\1', 'i') ELSE name END)), 'hex'))); +CREATE UNIQUE INDEX databases_field_uniq_names_idx ON metaschema_public.field (table_id, (decode(md5(lower(CASE + WHEN type = 'uuid' THEN regexp_replace(name, '^(.+?)(_row_id|_id|_uuid|_fk|_pk)$', E'\\1', 'i') + ELSE name +END)), 'hex'))); CREATE TABLE metaschema_public.foreign_key_constraint ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), @@ -208,6 +216,10 @@ CREATE TABLE metaschema_public.foreign_key_constraint ( ref_field_ids uuid[] NOT NULL, delete_action char(1) DEFAULT 'c', update_action char(1) DEFAULT 'a', + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) @@ -270,6 +282,11 @@ CREATE TABLE metaschema_public.index ( index_params jsonb, where_clause jsonb, is_unique boolean NOT NULL DEFAULT false, + smart_tags jsonb, + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) @@ -326,8 +343,13 @@ CREATE TABLE metaschema_public.policy ( privilege text, permissive boolean DEFAULT true, disabled boolean DEFAULT false, - template text, + policy_type text, data jsonb, + smart_tags jsonb, + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) @@ -354,6 +376,11 @@ CREATE TABLE metaschema_public.primary_key_constraint ( name text, type text, field_ids uuid[] NOT NULL, + smart_tags jsonb, + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) @@ -383,6 +410,11 @@ CREATE TABLE metaschema_public.procedure ( argdefaults text[], lang_name text, definition text, + smart_tags jsonb, + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) @@ -394,35 +426,6 @@ COMMENT ON CONSTRAINT db_fkey ON metaschema_public.procedure IS '@omit manyToMan CREATE INDEX procedure_database_id_idx ON metaschema_public.procedure (database_id); -CREATE TABLE metaschema_public.rls_function ( - id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), - database_id uuid NOT NULL DEFAULT uuid_nil(), - table_id uuid NOT NULL, - name text, - label text, - description text, - data jsonb, - inline boolean DEFAULT false, - security int DEFAULT 0, - CONSTRAINT db_fkey - FOREIGN KEY(database_id) - REFERENCES metaschema_public.database (id) - ON DELETE CASCADE, - CONSTRAINT table_fkey - FOREIGN KEY(table_id) - REFERENCES metaschema_public."table" (id) - ON DELETE CASCADE, - UNIQUE (database_id, name) -); - -COMMENT ON CONSTRAINT db_fkey ON metaschema_public.rls_function IS '@omit manyToMany'; - -COMMENT ON CONSTRAINT table_fkey ON metaschema_public.rls_function IS '@omit manyToMany'; - -CREATE INDEX rls_function_table_id_idx ON metaschema_public.rls_function (table_id); - -CREATE INDEX rls_function_database_id_idx ON metaschema_public.rls_function (database_id); - CREATE TABLE metaschema_public.schema_grant ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), database_id uuid NOT NULL DEFAULT uuid_nil(), @@ -501,6 +504,11 @@ CREATE TABLE metaschema_public.trigger ( name text NOT NULL, event text, function_name text, + smart_tags jsonb, + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) @@ -529,6 +537,10 @@ CREATE TABLE metaschema_public.unique_constraint ( smart_tags jsonb, type text, field_ids uuid[] NOT NULL, + category metaschema_public.object_category NOT NULL DEFAULT 'app', + module text NULL, + scope int NULL, + tags citext[] NOT NULL DEFAULT '{}', CONSTRAINT db_fkey FOREIGN KEY(database_id) REFERENCES metaschema_public.database (id) diff --git a/packages/metaschema-schema/verify/schemas/metaschema_public/types/object_category.sql b/packages/metaschema-schema/verify/schemas/metaschema_public/types/object_category.sql index 9d5c82f5d..cb6b1b13e 100644 --- a/packages/metaschema-schema/verify/schemas/metaschema_public/types/object_category.sql +++ b/packages/metaschema-schema/verify/schemas/metaschema_public/types/object_category.sql @@ -3,5 +3,7 @@ BEGIN; SELECT 'core'::metaschema_public.object_category; +SELECT 'module'::metaschema_public.object_category; +SELECT 'app'::metaschema_public.object_category; ROLLBACK; diff --git a/packages/services/deploy/schemas/services_public/tables/api_modules/table.sql b/packages/services/deploy/schemas/services_public/tables/api_modules/table.sql index 57cc1b3f9..e95741f97 100644 --- a/packages/services/deploy/schemas/services_public/tables/api_modules/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/api_modules/table.sql @@ -19,6 +19,13 @@ CREATE TABLE services_public.api_modules ( ); +COMMENT ON TABLE services_public.api_modules IS 'Server-side module configuration for an API endpoint; stores module name and JSON settings used by the application server'; +COMMENT ON COLUMN services_public.api_modules.id IS 'Unique identifier for this API module record'; +COMMENT ON COLUMN services_public.api_modules.database_id IS 'Reference to the metaschema database'; +COMMENT ON COLUMN services_public.api_modules.api_id IS 'API this module configuration belongs to'; +COMMENT ON COLUMN services_public.api_modules.name IS 'Module name (e.g. auth, uploads, webhooks)'; +COMMENT ON COLUMN services_public.api_modules.data IS 'JSON configuration data for this module'; + ALTER TABLE services_public.api_modules ADD CONSTRAINT api_modules_api_id_fkey FOREIGN KEY ( api_id ) REFERENCES services_public.apis ( id ); COMMENT ON CONSTRAINT api_modules_api_id_fkey ON services_public.api_modules IS E'@omit manyToMany'; CREATE INDEX api_modules_api_id_idx ON services_public.api_modules ( api_id ); diff --git a/packages/services/deploy/schemas/services_public/tables/api_schemas/table.sql b/packages/services/deploy/schemas/services_public/tables/api_schemas/table.sql index 6ed0da289..4530e72a9 100644 --- a/packages/services/deploy/schemas/services_public/tables/api_schemas/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/api_schemas/table.sql @@ -18,6 +18,12 @@ CREATE TABLE services_public.api_schemas ( unique(api_id, schema_id) ); +COMMENT ON TABLE services_public.api_schemas IS 'Join table linking APIs to the database schemas they expose; controls which schemas are accessible through each API'; +COMMENT ON COLUMN services_public.api_schemas.id IS 'Unique identifier for this API-schema mapping'; +COMMENT ON COLUMN services_public.api_schemas.database_id IS 'Reference to the metaschema database'; +COMMENT ON COLUMN services_public.api_schemas.schema_id IS 'Metaschema schema being exposed through the API'; +COMMENT ON COLUMN services_public.api_schemas.api_id IS 'API that exposes this schema'; + -- COMMENT ON CONSTRAINT schema_fkey ON services_public.api_schemas IS E'@omit manyToMany'; -- COMMENT ON CONSTRAINT api_fkey ON services_public.api_schemas IS E'@omit manyToMany'; COMMENT ON CONSTRAINT db_fkey ON services_public.api_schemas IS E'@omit manyToMany'; diff --git a/packages/services/deploy/schemas/services_public/tables/apis/table.sql b/packages/services/deploy/schemas/services_public/tables/apis/table.sql index 14d2a8141..b8c907590 100644 --- a/packages/services/deploy/schemas/services_public/tables/apis/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/apis/table.sql @@ -20,6 +20,15 @@ CREATE TABLE services_public.apis ( UNIQUE(database_id, name) ); +COMMENT ON TABLE services_public.apis IS 'API endpoint configurations: each record defines a PostGraphile/PostgREST API with its database role and public access settings'; +COMMENT ON COLUMN services_public.apis.id IS 'Unique identifier for this API'; +COMMENT ON COLUMN services_public.apis.database_id IS 'Reference to the metaschema database this API serves'; +COMMENT ON COLUMN services_public.apis.name IS 'Unique name for this API within its database'; +COMMENT ON COLUMN services_public.apis.dbname IS 'PostgreSQL database name to connect to'; +COMMENT ON COLUMN services_public.apis.role_name IS 'PostgreSQL role used for authenticated requests'; +COMMENT ON COLUMN services_public.apis.anon_role IS 'PostgreSQL role used for anonymous/unauthenticated requests'; +COMMENT ON COLUMN services_public.apis.is_public IS 'Whether this API is publicly accessible without authentication'; + COMMENT ON CONSTRAINT db_fkey ON services_public.apis IS E'@omit manyToMany'; CREATE INDEX apis_database_id_idx ON services_public.apis ( database_id ); diff --git a/packages/services/deploy/schemas/services_public/tables/apps/table.sql b/packages/services/deploy/schemas/services_public/tables/apps/table.sql index 1dbfcc576..1858d10d7 100644 --- a/packages/services/deploy/schemas/services_public/tables/apps/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/apps/table.sql @@ -23,6 +23,17 @@ CREATE TABLE services_public.apps ( UNIQUE ( site_id ) ); +COMMENT ON TABLE services_public.apps IS 'Mobile and native app configuration linked to a site, including store links and identifiers'; +COMMENT ON COLUMN services_public.apps.id IS 'Unique identifier for this app'; +COMMENT ON COLUMN services_public.apps.database_id IS 'Reference to the metaschema database this app belongs to'; +COMMENT ON COLUMN services_public.apps.site_id IS 'Site this app is associated with (one app per site)'; +COMMENT ON COLUMN services_public.apps.name IS 'Display name of the app'; +COMMENT ON COLUMN services_public.apps.app_image IS 'App icon or promotional image'; +COMMENT ON COLUMN services_public.apps.app_store_link IS 'URL to the Apple App Store listing'; +COMMENT ON COLUMN services_public.apps.app_store_id IS 'Apple App Store application identifier'; +COMMENT ON COLUMN services_public.apps.app_id_prefix IS 'Apple App ID prefix (Team ID) for universal links and associated domains'; +COMMENT ON COLUMN services_public.apps.play_store_link IS 'URL to the Google Play Store listing'; + ALTER TABLE services_public.apps ADD CONSTRAINT apps_site_id_fkey FOREIGN KEY ( site_id ) REFERENCES services_public.sites ( id ); COMMENT ON CONSTRAINT apps_site_id_fkey ON services_public.apps IS E'@omit manyToMany'; CREATE INDEX apps_site_id_idx ON services_public.apps ( site_id ); diff --git a/packages/services/deploy/schemas/services_public/tables/domains/table.sql b/packages/services/deploy/schemas/services_public/tables/domains/table.sql index 708966f78..26aa06f38 100644 --- a/packages/services/deploy/schemas/services_public/tables/domains/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/domains/table.sql @@ -29,6 +29,14 @@ CREATE TABLE services_public.domains ( UNIQUE ( subdomain, domain ) ); +COMMENT ON TABLE services_public.domains IS 'DNS domain and subdomain routing: maps hostnames to either an API endpoint or a site'; +COMMENT ON COLUMN services_public.domains.id IS 'Unique identifier for this domain record'; +COMMENT ON COLUMN services_public.domains.database_id IS 'Reference to the metaschema database this domain belongs to'; +COMMENT ON COLUMN services_public.domains.api_id IS 'API endpoint this domain routes to (mutually exclusive with site_id)'; +COMMENT ON COLUMN services_public.domains.site_id IS 'Site this domain routes to (mutually exclusive with api_id)'; +COMMENT ON COLUMN services_public.domains.subdomain IS 'Subdomain portion of the hostname'; +COMMENT ON COLUMN services_public.domains.domain IS 'Root domain of the hostname'; + COMMENT ON CONSTRAINT db_fkey ON services_public.domains IS E'@omit manyToMany'; CREATE INDEX domains_database_id_idx ON services_public.domains ( database_id ); diff --git a/packages/services/deploy/schemas/services_public/tables/site_metadata/table.sql b/packages/services/deploy/schemas/services_public/tables/site_metadata/table.sql index a90fb92b3..9d6d5afcb 100644 --- a/packages/services/deploy/schemas/services_public/tables/site_metadata/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/site_metadata/table.sql @@ -23,6 +23,14 @@ CREATE TABLE services_public.site_metadata ( ); +COMMENT ON TABLE services_public.site_metadata IS 'SEO and social sharing metadata for a site: page title, description, and Open Graph image'; +COMMENT ON COLUMN services_public.site_metadata.id IS 'Unique identifier for this metadata record'; +COMMENT ON COLUMN services_public.site_metadata.database_id IS 'Reference to the metaschema database'; +COMMENT ON COLUMN services_public.site_metadata.site_id IS 'Site this metadata belongs to'; +COMMENT ON COLUMN services_public.site_metadata.title IS 'Page title for SEO (max 120 characters)'; +COMMENT ON COLUMN services_public.site_metadata.description IS 'Meta description for SEO and social sharing (max 120 characters)'; +COMMENT ON COLUMN services_public.site_metadata.og_image IS 'Open Graph image for social media previews'; + ALTER TABLE services_public.site_metadata ADD CONSTRAINT site_metadata_site_id_fkey FOREIGN KEY ( site_id ) REFERENCES services_public.sites ( id ); COMMENT ON CONSTRAINT site_metadata_site_id_fkey ON services_public.site_metadata IS E'@omit manyToMany'; CREATE INDEX site_metadata_site_id_idx ON services_public.site_metadata ( site_id ); diff --git a/packages/services/deploy/schemas/services_public/tables/site_modules/table.sql b/packages/services/deploy/schemas/services_public/tables/site_modules/table.sql index 565c3aee2..0fc227459 100644 --- a/packages/services/deploy/schemas/services_public/tables/site_modules/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/site_modules/table.sql @@ -17,6 +17,13 @@ CREATE TABLE services_public.site_modules ( CONSTRAINT db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE ); +COMMENT ON TABLE services_public.site_modules IS 'Site-level module configuration; stores module name and JSON settings used by the frontend or server for each site'; +COMMENT ON COLUMN services_public.site_modules.id IS 'Unique identifier for this site module record'; +COMMENT ON COLUMN services_public.site_modules.database_id IS 'Reference to the metaschema database'; +COMMENT ON COLUMN services_public.site_modules.site_id IS 'Site this module configuration belongs to'; +COMMENT ON COLUMN services_public.site_modules.name IS 'Module name (e.g. user_auth_module, analytics)'; +COMMENT ON COLUMN services_public.site_modules.data IS 'JSON configuration data for this module'; + ALTER TABLE services_public.site_modules ADD CONSTRAINT site_modules_site_id_fkey FOREIGN KEY ( site_id ) REFERENCES services_public.sites ( id ); COMMENT ON CONSTRAINT site_modules_site_id_fkey ON services_public.site_modules IS E'@omit manyToMany'; CREATE INDEX site_modules_site_id_idx ON services_public.site_modules ( site_id ); diff --git a/packages/services/deploy/schemas/services_public/tables/site_themes/table.sql b/packages/services/deploy/schemas/services_public/tables/site_themes/table.sql index 6fe579aec..2c1fd17e2 100644 --- a/packages/services/deploy/schemas/services_public/tables/site_themes/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/site_themes/table.sql @@ -17,6 +17,12 @@ CREATE TABLE services_public.site_themes ( CONSTRAINT db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE ); +COMMENT ON TABLE services_public.site_themes IS 'Theme configuration for a site; stores design tokens, colors, and typography as JSONB'; +COMMENT ON COLUMN services_public.site_themes.id IS 'Unique identifier for this theme record'; +COMMENT ON COLUMN services_public.site_themes.database_id IS 'Reference to the metaschema database'; +COMMENT ON COLUMN services_public.site_themes.site_id IS 'Site this theme belongs to'; +COMMENT ON COLUMN services_public.site_themes.theme IS 'JSONB object containing theme tokens (colors, typography, spacing, etc.)'; + ALTER TABLE services_public.site_themes ADD CONSTRAINT site_themes_site_id_fkey FOREIGN KEY ( site_id ) REFERENCES services_public.sites ( id ); COMMENT ON CONSTRAINT site_themes_site_id_fkey ON services_public.site_themes IS E'@omit manyToMany'; CREATE INDEX site_themes_site_id_idx ON services_public.site_themes ( site_id ); diff --git a/packages/services/deploy/schemas/services_public/tables/sites/table.sql b/packages/services/deploy/schemas/services_public/tables/sites/table.sql index beb95d949..c9bccfc82 100644 --- a/packages/services/deploy/schemas/services_public/tables/sites/table.sql +++ b/packages/services/deploy/schemas/services_public/tables/sites/table.sql @@ -24,6 +24,17 @@ CREATE TABLE services_public.sites ( CONSTRAINT max_descr CHECK ( character_length(description) <= 120 ) ); +COMMENT ON TABLE services_public.sites IS 'Top-level site configuration: branding assets, title, and description for a deployed application'; +COMMENT ON COLUMN services_public.sites.id IS 'Unique identifier for this site'; +COMMENT ON COLUMN services_public.sites.database_id IS 'Reference to the metaschema database this site belongs to'; +COMMENT ON COLUMN services_public.sites.title IS 'Display title for the site (max 120 characters)'; +COMMENT ON COLUMN services_public.sites.description IS 'Short description of the site (max 120 characters)'; +COMMENT ON COLUMN services_public.sites.og_image IS 'Open Graph image used for social media link previews'; +COMMENT ON COLUMN services_public.sites.favicon IS 'Browser favicon attachment'; +COMMENT ON COLUMN services_public.sites.apple_touch_icon IS 'Apple touch icon for iOS home screen bookmarks'; +COMMENT ON COLUMN services_public.sites.logo IS 'Primary logo image for the site'; +COMMENT ON COLUMN services_public.sites.dbname IS 'PostgreSQL database name this site connects to'; + COMMENT ON CONSTRAINT db_fkey ON services_public.sites IS E'@omit manyToMany'; CREATE INDEX sites_database_id_idx ON services_public.sites ( database_id ); diff --git a/packages/types/sql/pgpm-types--0.15.5.sql b/packages/types/sql/pgpm-types--0.15.5.sql new file mode 100644 index 000000000..76d524a4d --- /dev/null +++ b/packages/types/sql/pgpm-types--0.15.5.sql @@ -0,0 +1,71 @@ +\echo Use "CREATE EXTENSION pgpm-types" to load this file. \quit +CREATE DOMAIN attachment AS text + CHECK (value ~ E'^https?://[^\\s]+$'); + +COMMENT ON DOMAIN attachment IS '@name constructiveInternalTypeAttachment'; + +CREATE DOMAIN email AS citext + CHECK (value ~ '@'); + +COMMENT ON DOMAIN email IS '@name constructiveInternalTypeEmail'; + +CREATE DOMAIN hostname AS text + CHECK (value ~ E'^[^\\s]+$'); + +COMMENT ON DOMAIN hostname IS '@name constructiveInternalTypeHostname'; + +CREATE DOMAIN image AS jsonb + CHECK ( + jsonb_typeof(value) = 'object' + AND (value ? 'url' + OR value ? 'id' + OR value ? 'key') + AND (NOT (value ? 'url') + OR (value ->> 'url') ~ E'^https?://[^\\s]+$') + AND (NOT (value ? 'id') + OR jsonb_typeof(value -> 'id') = 'string') + AND (NOT (value ? 'key') + OR jsonb_typeof(value -> 'key') = 'string') + AND (NOT (value ? 'bucket') + OR jsonb_typeof(value -> 'bucket') = 'string') + AND (NOT (value ? 'provider') + OR jsonb_typeof(value -> 'provider') = 'string') + AND (NOT (value ? 'mime') + OR jsonb_typeof(value -> 'mime') = 'string') + AND (NOT (value ? 'versions') + OR jsonb_typeof(value -> 'versions') = 'array') +); + +COMMENT ON DOMAIN image IS '@name constructiveInternalTypeImage'; + +CREATE DOMAIN origin AS text + CHECK (value ~ E'^https?://[^/\\s]+$'); + +COMMENT ON DOMAIN origin IS '@name constructiveInternalTypeOrigin'; + +CREATE DOMAIN upload AS jsonb + CHECK ( + jsonb_typeof(value) = 'object' + AND (value ? 'url' + OR value ? 'id' + OR value ? 'key') + AND (NOT (value ? 'url') + OR (value ->> 'url') ~ E'^https?://[^\\s]+$') + AND (NOT (value ? 'id') + OR jsonb_typeof(value -> 'id') = 'string') + AND (NOT (value ? 'key') + OR jsonb_typeof(value -> 'key') = 'string') + AND (NOT (value ? 'bucket') + OR jsonb_typeof(value -> 'bucket') = 'string') + AND (NOT (value ? 'provider') + OR jsonb_typeof(value -> 'provider') = 'string') + AND (NOT (value ? 'mime') + OR jsonb_typeof(value -> 'mime') = 'string') +); + +COMMENT ON DOMAIN upload IS '@name constructiveInternalTypeUpload'; + +CREATE DOMAIN url AS text + CHECK (value ~ E'^https?://[^\\s]+$'); + +COMMENT ON DOMAIN url IS '@name constructiveInternalTypeUrl'; \ No newline at end of file