@pgpm/services 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,9 +7,7 @@ exports[`services functionality should handle complete meta workflow with servic
7
7
  "label": null,
8
8
  "name": "my-meta-db",
9
9
  "owner_id": "[ID]",
10
- "private_schema_name": null,
11
10
  "schema_hash": null,
12
- "schema_name": null,
13
11
  }
14
12
  `;
15
13
 
@@ -0,0 +1,47 @@
1
+ -- Deploy schemas/services_private/triggers/enforce_api_schema_table_name_uniqueness to pg
2
+
3
+ -- requires: schemas/services_private/schema
4
+ -- requires: schemas/services_public/tables/api_schemas/table
5
+ -- requires: metaschema-schema:schemas/metaschema_public/tables/table/table
6
+ -- requires: metaschema-schema:schemas/metaschema_public/tables/table/indexes/databases_table_unique_name_idx
7
+
8
+ BEGIN;
9
+
10
+ -- When linking a schema to an API, check that none of its tables conflict
11
+ -- (by plural-hash) with tables already exposed through that API's other schemas.
12
+ CREATE FUNCTION services_private.tg_enforce_api_schema_table_name_uniqueness()
13
+ RETURNS TRIGGER AS $$
14
+ DECLARE
15
+ conflicting_new_table text;
16
+ conflicting_existing_table text;
17
+ BEGIN
18
+ -- Find any table name collision between the newly linked schema
19
+ -- and any schema already linked to the same API
20
+ SELECT new_t.name, existing_t.name
21
+ INTO conflicting_new_table, conflicting_existing_table
22
+ FROM metaschema_public.table AS new_t
23
+ JOIN services_public.api_schemas AS existing_link
24
+ ON existing_link.api_id = NEW.api_id
25
+ AND existing_link.schema_id IS DISTINCT FROM NEW.schema_id
26
+ JOIN metaschema_public.table AS existing_t
27
+ ON existing_t.schema_id = existing_link.schema_id
28
+ AND metaschema_private.table_name_hash(existing_t.name) = metaschema_private.table_name_hash(new_t.name)
29
+ WHERE new_t.schema_id = NEW.schema_id
30
+ LIMIT 1;
31
+
32
+ IF conflicting_new_table IS NOT NULL THEN
33
+ RAISE EXCEPTION 'Cannot link schema to API: table "%" conflicts with existing table "%" already exposed in this API. Table names must be unique (by plural form) across all schemas within the same API.',
34
+ conflicting_new_table, conflicting_existing_table;
35
+ END IF;
36
+
37
+ RETURN NEW;
38
+ END;
39
+ $$
40
+ LANGUAGE plpgsql VOLATILE;
41
+
42
+ CREATE TRIGGER _000001_enforce_api_schema_table_name_uniqueness
43
+ BEFORE INSERT ON services_public.api_schemas
44
+ FOR EACH ROW
45
+ EXECUTE FUNCTION services_private.tg_enforce_api_schema_table_name_uniqueness();
46
+
47
+ COMMIT;
@@ -0,0 +1,60 @@
1
+ -- Deploy schemas/services_private/triggers/enforce_api_table_name_uniqueness to pg
2
+
3
+ -- requires: schemas/services_private/schema
4
+ -- requires: schemas/services_public/tables/api_schemas/table
5
+ -- requires: metaschema-schema:schemas/metaschema_public/tables/table/table
6
+ -- requires: metaschema-schema:schemas/metaschema_public/tables/table/indexes/databases_table_unique_name_idx
7
+
8
+ BEGIN;
9
+
10
+ -- Enforce that table names are unique (by plural-hash) across all schemas within each API.
11
+ -- This allows different APIs to have tables with the same name, but prevents
12
+ -- collisions within a single API where multiple schemas are exposed together.
13
+ CREATE FUNCTION services_private.tg_enforce_api_table_name_uniqueness()
14
+ RETURNS TRIGGER AS $$
15
+ DECLARE
16
+ new_name_hash bytea;
17
+ conflicting_api_name text;
18
+ conflicting_table_name text;
19
+ BEGIN
20
+ -- Compute the plural-hash of the new table name
21
+ new_name_hash := metaschema_private.table_name_hash(NEW.name);
22
+
23
+ -- Check if any API that includes this table's schema also includes
24
+ -- another schema containing a table with the same name hash
25
+ SELECT a.name, t.name
26
+ INTO conflicting_api_name, conflicting_table_name
27
+ FROM services_public.api_schemas AS my_api
28
+ JOIN services_public.api_schemas AS other_api
29
+ ON other_api.api_id = my_api.api_id
30
+ AND other_api.schema_id IS DISTINCT FROM NEW.schema_id
31
+ JOIN metaschema_public.table AS t
32
+ ON t.schema_id = other_api.schema_id
33
+ AND metaschema_private.table_name_hash(t.name) = new_name_hash
34
+ JOIN services_public.apis AS a
35
+ ON a.id = my_api.api_id
36
+ WHERE my_api.schema_id = NEW.schema_id
37
+ LIMIT 1;
38
+
39
+ IF conflicting_api_name IS NOT NULL THEN
40
+ RAISE EXCEPTION 'Table name "%" conflicts with existing table "%" in API "%". Table names must be unique (by plural form) across all schemas within the same API.',
41
+ NEW.name, conflicting_table_name, conflicting_api_name;
42
+ END IF;
43
+
44
+ RETURN NEW;
45
+ END;
46
+ $$
47
+ LANGUAGE plpgsql VOLATILE;
48
+
49
+ CREATE TRIGGER _000003_enforce_api_table_name_uniqueness
50
+ BEFORE INSERT ON metaschema_public.table
51
+ FOR EACH ROW
52
+ EXECUTE FUNCTION services_private.tg_enforce_api_table_name_uniqueness();
53
+
54
+ CREATE TRIGGER _000003_enforce_api_table_name_uniqueness_update
55
+ BEFORE UPDATE ON metaschema_public.table
56
+ FOR EACH ROW
57
+ WHEN (NEW.name IS DISTINCT FROM OLD.name OR NEW.schema_id IS DISTINCT FROM OLD.schema_id)
58
+ EXECUTE FUNCTION services_private.tg_enforce_api_table_name_uniqueness();
59
+
60
+ COMMIT;
@@ -19,6 +19,13 @@ CREATE TABLE services_public.api_modules (
19
19
 
20
20
  );
21
21
 
22
+ 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';
23
+ COMMENT ON COLUMN services_public.api_modules.id IS 'Unique identifier for this API module record';
24
+ COMMENT ON COLUMN services_public.api_modules.database_id IS 'Reference to the metaschema database';
25
+ COMMENT ON COLUMN services_public.api_modules.api_id IS 'API this module configuration belongs to';
26
+ COMMENT ON COLUMN services_public.api_modules.name IS 'Module name (e.g. auth, uploads, webhooks)';
27
+ COMMENT ON COLUMN services_public.api_modules.data IS 'JSON configuration data for this module';
28
+
22
29
  ALTER TABLE services_public.api_modules ADD CONSTRAINT api_modules_api_id_fkey FOREIGN KEY ( api_id ) REFERENCES services_public.apis ( id );
23
30
  COMMENT ON CONSTRAINT api_modules_api_id_fkey ON services_public.api_modules IS E'@omit manyToMany';
24
31
  CREATE INDEX api_modules_api_id_idx ON services_public.api_modules ( api_id );
@@ -18,6 +18,12 @@ CREATE TABLE services_public.api_schemas (
18
18
  unique(api_id, schema_id)
19
19
  );
20
20
 
21
+ 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';
22
+ COMMENT ON COLUMN services_public.api_schemas.id IS 'Unique identifier for this API-schema mapping';
23
+ COMMENT ON COLUMN services_public.api_schemas.database_id IS 'Reference to the metaschema database';
24
+ COMMENT ON COLUMN services_public.api_schemas.schema_id IS 'Metaschema schema being exposed through the API';
25
+ COMMENT ON COLUMN services_public.api_schemas.api_id IS 'API that exposes this schema';
26
+
21
27
  -- COMMENT ON CONSTRAINT schema_fkey ON services_public.api_schemas IS E'@omit manyToMany';
22
28
  -- COMMENT ON CONSTRAINT api_fkey ON services_public.api_schemas IS E'@omit manyToMany';
23
29
  COMMENT ON CONSTRAINT db_fkey ON services_public.api_schemas IS E'@omit manyToMany';
@@ -20,6 +20,15 @@ CREATE TABLE services_public.apis (
20
20
  UNIQUE(database_id, name)
21
21
  );
22
22
 
23
+ 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';
24
+ COMMENT ON COLUMN services_public.apis.id IS 'Unique identifier for this API';
25
+ COMMENT ON COLUMN services_public.apis.database_id IS 'Reference to the metaschema database this API serves';
26
+ COMMENT ON COLUMN services_public.apis.name IS 'Unique name for this API within its database';
27
+ COMMENT ON COLUMN services_public.apis.dbname IS 'PostgreSQL database name to connect to';
28
+ COMMENT ON COLUMN services_public.apis.role_name IS 'PostgreSQL role used for authenticated requests';
29
+ COMMENT ON COLUMN services_public.apis.anon_role IS 'PostgreSQL role used for anonymous/unauthenticated requests';
30
+ COMMENT ON COLUMN services_public.apis.is_public IS 'Whether this API is publicly accessible without authentication';
31
+
23
32
  COMMENT ON CONSTRAINT db_fkey ON services_public.apis IS E'@omit manyToMany';
24
33
  CREATE INDEX apis_database_id_idx ON services_public.apis ( database_id );
25
34
 
@@ -23,6 +23,17 @@ CREATE TABLE services_public.apps (
23
23
  UNIQUE ( site_id )
24
24
  );
25
25
 
26
+ COMMENT ON TABLE services_public.apps IS 'Mobile and native app configuration linked to a site, including store links and identifiers';
27
+ COMMENT ON COLUMN services_public.apps.id IS 'Unique identifier for this app';
28
+ COMMENT ON COLUMN services_public.apps.database_id IS 'Reference to the metaschema database this app belongs to';
29
+ COMMENT ON COLUMN services_public.apps.site_id IS 'Site this app is associated with (one app per site)';
30
+ COMMENT ON COLUMN services_public.apps.name IS 'Display name of the app';
31
+ COMMENT ON COLUMN services_public.apps.app_image IS 'App icon or promotional image';
32
+ COMMENT ON COLUMN services_public.apps.app_store_link IS 'URL to the Apple App Store listing';
33
+ COMMENT ON COLUMN services_public.apps.app_store_id IS 'Apple App Store application identifier';
34
+ COMMENT ON COLUMN services_public.apps.app_id_prefix IS 'Apple App ID prefix (Team ID) for universal links and associated domains';
35
+ COMMENT ON COLUMN services_public.apps.play_store_link IS 'URL to the Google Play Store listing';
36
+
26
37
  ALTER TABLE services_public.apps ADD CONSTRAINT apps_site_id_fkey FOREIGN KEY ( site_id ) REFERENCES services_public.sites ( id );
27
38
  COMMENT ON CONSTRAINT apps_site_id_fkey ON services_public.apps IS E'@omit manyToMany';
28
39
  CREATE INDEX apps_site_id_idx ON services_public.apps ( site_id );
@@ -29,6 +29,14 @@ CREATE TABLE services_public.domains (
29
29
  UNIQUE ( subdomain, domain )
30
30
  );
31
31
 
32
+ COMMENT ON TABLE services_public.domains IS 'DNS domain and subdomain routing: maps hostnames to either an API endpoint or a site';
33
+ COMMENT ON COLUMN services_public.domains.id IS 'Unique identifier for this domain record';
34
+ COMMENT ON COLUMN services_public.domains.database_id IS 'Reference to the metaschema database this domain belongs to';
35
+ COMMENT ON COLUMN services_public.domains.api_id IS 'API endpoint this domain routes to (mutually exclusive with site_id)';
36
+ COMMENT ON COLUMN services_public.domains.site_id IS 'Site this domain routes to (mutually exclusive with api_id)';
37
+ COMMENT ON COLUMN services_public.domains.subdomain IS 'Subdomain portion of the hostname';
38
+ COMMENT ON COLUMN services_public.domains.domain IS 'Root domain of the hostname';
39
+
32
40
  COMMENT ON CONSTRAINT db_fkey ON services_public.domains IS E'@omit manyToMany';
33
41
  CREATE INDEX domains_database_id_idx ON services_public.domains ( database_id );
34
42
 
@@ -23,6 +23,14 @@ CREATE TABLE services_public.site_metadata (
23
23
  );
24
24
 
25
25
 
26
+ COMMENT ON TABLE services_public.site_metadata IS 'SEO and social sharing metadata for a site: page title, description, and Open Graph image';
27
+ COMMENT ON COLUMN services_public.site_metadata.id IS 'Unique identifier for this metadata record';
28
+ COMMENT ON COLUMN services_public.site_metadata.database_id IS 'Reference to the metaschema database';
29
+ COMMENT ON COLUMN services_public.site_metadata.site_id IS 'Site this metadata belongs to';
30
+ COMMENT ON COLUMN services_public.site_metadata.title IS 'Page title for SEO (max 120 characters)';
31
+ COMMENT ON COLUMN services_public.site_metadata.description IS 'Meta description for SEO and social sharing (max 120 characters)';
32
+ COMMENT ON COLUMN services_public.site_metadata.og_image IS 'Open Graph image for social media previews';
33
+
26
34
  ALTER TABLE services_public.site_metadata ADD CONSTRAINT site_metadata_site_id_fkey FOREIGN KEY ( site_id ) REFERENCES services_public.sites ( id );
27
35
  COMMENT ON CONSTRAINT site_metadata_site_id_fkey ON services_public.site_metadata IS E'@omit manyToMany';
28
36
  CREATE INDEX site_metadata_site_id_idx ON services_public.site_metadata ( site_id );
@@ -17,6 +17,13 @@ CREATE TABLE services_public.site_modules (
17
17
  CONSTRAINT db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE
18
18
  );
19
19
 
20
+ 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';
21
+ COMMENT ON COLUMN services_public.site_modules.id IS 'Unique identifier for this site module record';
22
+ COMMENT ON COLUMN services_public.site_modules.database_id IS 'Reference to the metaschema database';
23
+ COMMENT ON COLUMN services_public.site_modules.site_id IS 'Site this module configuration belongs to';
24
+ COMMENT ON COLUMN services_public.site_modules.name IS 'Module name (e.g. user_auth_module, analytics)';
25
+ COMMENT ON COLUMN services_public.site_modules.data IS 'JSON configuration data for this module';
26
+
20
27
  ALTER TABLE services_public.site_modules ADD CONSTRAINT site_modules_site_id_fkey FOREIGN KEY ( site_id ) REFERENCES services_public.sites ( id );
21
28
  COMMENT ON CONSTRAINT site_modules_site_id_fkey ON services_public.site_modules IS E'@omit manyToMany';
22
29
  CREATE INDEX site_modules_site_id_idx ON services_public.site_modules ( site_id );
@@ -17,6 +17,12 @@ CREATE TABLE services_public.site_themes (
17
17
  CONSTRAINT db_fkey FOREIGN KEY (database_id) REFERENCES metaschema_public.database (id) ON DELETE CASCADE
18
18
  );
19
19
 
20
+ COMMENT ON TABLE services_public.site_themes IS 'Theme configuration for a site; stores design tokens, colors, and typography as JSONB';
21
+ COMMENT ON COLUMN services_public.site_themes.id IS 'Unique identifier for this theme record';
22
+ COMMENT ON COLUMN services_public.site_themes.database_id IS 'Reference to the metaschema database';
23
+ COMMENT ON COLUMN services_public.site_themes.site_id IS 'Site this theme belongs to';
24
+ COMMENT ON COLUMN services_public.site_themes.theme IS 'JSONB object containing theme tokens (colors, typography, spacing, etc.)';
25
+
20
26
  ALTER TABLE services_public.site_themes ADD CONSTRAINT site_themes_site_id_fkey FOREIGN KEY ( site_id ) REFERENCES services_public.sites ( id );
21
27
  COMMENT ON CONSTRAINT site_themes_site_id_fkey ON services_public.site_themes IS E'@omit manyToMany';
22
28
  CREATE INDEX site_themes_site_id_idx ON services_public.site_themes ( site_id );
@@ -24,6 +24,17 @@ CREATE TABLE services_public.sites (
24
24
  CONSTRAINT max_descr CHECK ( character_length(description) <= 120 )
25
25
  );
26
26
 
27
+ COMMENT ON TABLE services_public.sites IS 'Top-level site configuration: branding assets, title, and description for a deployed application';
28
+ COMMENT ON COLUMN services_public.sites.id IS 'Unique identifier for this site';
29
+ COMMENT ON COLUMN services_public.sites.database_id IS 'Reference to the metaschema database this site belongs to';
30
+ COMMENT ON COLUMN services_public.sites.title IS 'Display title for the site (max 120 characters)';
31
+ COMMENT ON COLUMN services_public.sites.description IS 'Short description of the site (max 120 characters)';
32
+ COMMENT ON COLUMN services_public.sites.og_image IS 'Open Graph image used for social media link previews';
33
+ COMMENT ON COLUMN services_public.sites.favicon IS 'Browser favicon attachment';
34
+ COMMENT ON COLUMN services_public.sites.apple_touch_icon IS 'Apple touch icon for iOS home screen bookmarks';
35
+ COMMENT ON COLUMN services_public.sites.logo IS 'Primary logo image for the site';
36
+ COMMENT ON COLUMN services_public.sites.dbname IS 'PostgreSQL database name this site connects to';
37
+
27
38
  COMMENT ON CONSTRAINT db_fkey ON services_public.sites IS E'@omit manyToMany';
28
39
  CREATE INDEX sites_database_id_idx ON services_public.sites ( database_id );
29
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgpm/services",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Services schemas for module registration and service configuration",
5
5
  "author": "Dan Lynch <pyramation@gmail.com>",
6
6
  "contributors": [
@@ -21,11 +21,11 @@
21
21
  "test:watch": "jest --watch"
22
22
  },
23
23
  "dependencies": {
24
- "@pgpm/metaschema-schema": "0.17.0",
25
- "@pgpm/verify": "0.17.0"
24
+ "@pgpm/metaschema-schema": "0.18.0",
25
+ "@pgpm/verify": "0.18.0"
26
26
  },
27
27
  "devDependencies": {
28
- "pgpm": "^1.3.0"
28
+ "pgpm": "^4.2.3"
29
29
  },
30
30
  "repository": {
31
31
  "type": "git",
@@ -35,5 +35,5 @@
35
35
  "bugs": {
36
36
  "url": "https://github.com/constructive-io/constructive-db/issues"
37
37
  },
38
- "gitHead": "8eb8b9e3a6784fb45a3a9e86838f8417f061925c"
38
+ "gitHead": "8144027c7fab4956bcdebd736d04c0d4f57344bc"
39
39
  }
package/pgpm.plan CHANGED
@@ -13,3 +13,5 @@ schemas/services_public/tables/domains/table [schemas/services_public/schema sch
13
13
  schemas/services_public/tables/site_metadata/table [schemas/services_public/schema schemas/services_public/tables/sites/table metaschema-schema:schemas/metaschema_public/tables/database/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/services_public/tables/site_metadata/table
14
14
  schemas/services_public/tables/site_modules/table [schemas/services_public/schema schemas/services_public/tables/sites/table metaschema-schema:schemas/metaschema_public/tables/database/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/services_public/tables/site_modules/table
15
15
  schemas/services_public/tables/site_themes/table [schemas/services_public/schema schemas/services_public/tables/sites/table metaschema-schema:schemas/metaschema_public/tables/database/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/services_public/tables/site_themes/table
16
+ schemas/services_private/triggers/enforce_api_table_name_uniqueness [schemas/services_private/schema schemas/services_public/tables/api_schemas/table metaschema-schema:schemas/metaschema_public/tables/table/table metaschema-schema:schemas/metaschema_public/tables/table/indexes/databases_table_unique_name_idx] 2026-02-27T00:00:00Z devin <devin@cognition.ai> # add API-level table name uniqueness trigger
17
+ schemas/services_private/triggers/enforce_api_schema_table_name_uniqueness [schemas/services_private/schema schemas/services_public/tables/api_schemas/table metaschema-schema:schemas/metaschema_public/tables/table/table metaschema-schema:schemas/metaschema_public/tables/table/indexes/databases_table_unique_name_idx] 2026-02-27T00:00:00Z devin <devin@cognition.ai> # add API-schema link table name uniqueness trigger
@@ -0,0 +1,8 @@
1
+ -- Revert schemas/services_private/triggers/enforce_api_schema_table_name_uniqueness
2
+
3
+ BEGIN;
4
+
5
+ DROP TRIGGER IF EXISTS _000001_enforce_api_schema_table_name_uniqueness ON services_public.api_schemas;
6
+ DROP FUNCTION IF EXISTS services_private.tg_enforce_api_schema_table_name_uniqueness();
7
+
8
+ COMMIT;
@@ -0,0 +1,9 @@
1
+ -- Revert schemas/services_private/triggers/enforce_api_table_name_uniqueness
2
+
3
+ BEGIN;
4
+
5
+ DROP TRIGGER IF EXISTS _000003_enforce_api_table_name_uniqueness_update ON metaschema_public.table;
6
+ DROP TRIGGER IF EXISTS _000003_enforce_api_table_name_uniqueness ON metaschema_public.table;
7
+ DROP FUNCTION IF EXISTS services_private.tg_enforce_api_table_name_uniqueness();
8
+
9
+ COMMIT;
@@ -0,0 +1,10 @@
1
+ -- Verify schemas/services_private/triggers/enforce_api_schema_table_name_uniqueness
2
+
3
+ BEGIN;
4
+
5
+ SELECT has_function_privilege(
6
+ 'services_private.tg_enforce_api_schema_table_name_uniqueness()',
7
+ 'execute'
8
+ );
9
+
10
+ ROLLBACK;
@@ -0,0 +1,10 @@
1
+ -- Verify schemas/services_private/triggers/enforce_api_table_name_uniqueness
2
+
3
+ BEGIN;
4
+
5
+ SELECT has_function_privilege(
6
+ 'services_private.tg_enforce_api_table_name_uniqueness()',
7
+ 'execute'
8
+ );
9
+
10
+ ROLLBACK;