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 a998803b..93072f12 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 @@ -13,7 +13,8 @@ INSERT INTO inflection.inflection_rules ('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|codebase|firebase|knowledgebase)s$', NULL), + ('plural', '(.+base)s$', NULL), + ('plural', '(database)s$', NULL), ('plural', '(drive)s$', NULL), ('plural', '(hi|ti)ves$', NULL), ('plural', '(curve)s$', NULL), @@ -105,7 +106,7 @@ INSERT INTO inflection.inflection_rules ('singular', '^(m|wom)en$', E'\\1an'), ('singular', '(pe)ople$', E'\\1rson'), ('singular', '(child)ren$', E'\\1'), - ('singular', '(database|codebase|firebase|knowledgebase)s$', E'\\1'), + ('singular', '(database)s$', E'\\1'), ('singular', '(drive)s$', E'\\1'), ('singular', '^genera$', E'genus'), ('singular', '^(criteri)a$', E'\\1on'), @@ -115,6 +116,7 @@ INSERT INTO inflection.inflection_rules ('singular', '(memorand)a$', E'\\1um'), ('singular', '(curricul)a$', E'\\1um'), ('singular', '([ti])a$', E'\\1um'), + ('singular', '(.+base)s$', E'\\1'), ('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'), ('singular', '(curve)s$', E'\\1'), diff --git a/packages/metaschema-modules/__tests__/__snapshots__/modules.test.ts.snap b/packages/metaschema-modules/__tests__/__snapshots__/modules.test.ts.snap index 42722cec..d38ce4a1 100644 --- a/packages/metaschema-modules/__tests__/__snapshots__/modules.test.ts.snap +++ b/packages/metaschema-modules/__tests__/__snapshots__/modules.test.ts.snap @@ -9,7 +9,6 @@ exports[`db_meta_modules should have all expected module tables 1`] = ` "default_ids_module", "emails_module", "encrypted_secrets_module", - "field_module", "hierarchy_module", "invites_module", "levels_module", @@ -22,6 +21,7 @@ exports[`db_meta_modules should have all expected module tables 1`] = ` "rls_module", "secrets_module", "sessions_module", + "storage_module", "table_template_module", "user_auth_module", "users_module", @@ -32,7 +32,7 @@ exports[`db_meta_modules should have all expected module tables 1`] = ` exports[`db_meta_modules should verify all module tables exist in metaschema_modules_public schema 1`] = ` { "moduleTablesCount": 22, - "totalTables": 27, + "totalTables": 28, } `; @@ -85,67 +85,6 @@ exports[`db_meta_modules should verify emails_module table structure 1`] = ` } `; -exports[`db_meta_modules should verify field_module table structure 1`] = ` -{ - "columns": [ - { - "column_default": "uuidv7()", - "column_name": "id", - "data_type": "uuid", - "is_nullable": "NO", - }, - { - "column_default": null, - "column_name": "database_id", - "data_type": "uuid", - "is_nullable": "NO", - }, - { - "column_default": "uuid_nil()", - "column_name": "private_schema_id", - "data_type": "uuid", - "is_nullable": "NO", - }, - { - "column_default": "uuid_nil()", - "column_name": "table_id", - "data_type": "uuid", - "is_nullable": "NO", - }, - { - "column_default": "uuid_nil()", - "column_name": "field_id", - "data_type": "uuid", - "is_nullable": "NO", - }, - { - "column_default": null, - "column_name": "node_type", - "data_type": "text", - "is_nullable": "NO", - }, - { - "column_default": "'{}'::jsonb", - "column_name": "data", - "data_type": "jsonb", - "is_nullable": "NO", - }, - { - "column_default": null, - "column_name": "triggers", - "data_type": "ARRAY", - "is_nullable": "YES", - }, - { - "column_default": null, - "column_name": "functions", - "data_type": "ARRAY", - "is_nullable": "YES", - }, - ], -} -`; - exports[`db_meta_modules should verify module table structures have database_id foreign keys 1`] = ` { "constraintCount": 64152, @@ -154,7 +93,7 @@ exports[`db_meta_modules should verify module table structures have database_id exports[`db_meta_modules should verify module tables have proper foreign key relationships 1`] = ` { - "constraintCount": 91658, + "constraintCount": 92266, "foreignTables": [ "database", "field", diff --git a/packages/metaschema-modules/__tests__/modules.test.ts b/packages/metaschema-modules/__tests__/modules.test.ts index 7246c8e8..52e542cb 100644 --- a/packages/metaschema-modules/__tests__/modules.test.ts +++ b/packages/metaschema-modules/__tests__/modules.test.ts @@ -28,7 +28,6 @@ describe('db_meta_modules', () => { 'default_ids_module', 'emails_module', 'encrypted_secrets_module', - 'field_module', 'invites_module', 'levels_module', 'limits_module', @@ -254,34 +253,6 @@ describe('db_meta_modules', () => { })).toMatchSnapshot(); }); - // Test for unified field_module with node_type - it('should verify field_module table structure', async () => { - const columns = await pg.any(` - SELECT - column_name, - data_type, - is_nullable, - column_default - FROM information_schema.columns - WHERE table_schema = 'metaschema_modules_public' - AND table_name = 'field_module' - ORDER BY ordinal_position - `); - - // Check that key columns exist - const columnNames = columns.map(c => c.column_name); - expect(columnNames).toContain('id'); - expect(columnNames).toContain('database_id'); - expect(columnNames).toContain('table_id'); - expect(columnNames).toContain('field_id'); - expect(columnNames).toContain('node_type'); - expect(columnNames).toContain('data'); - expect(columnNames).toContain('triggers'); - expect(columnNames).toContain('functions'); - - expect(snapshot({ columns })).toMatchSnapshot(); - }); - // Test for table_template_module with node_type for table creation templates it('should verify table_template_module table structure', async () => { const columns = await pg.any(` @@ -310,4 +281,4 @@ describe('db_meta_modules', () => { expect(snapshot({ columns })).toMatchSnapshot(); }); -}); \ No newline at end of file +}); \ No newline at end of file diff --git a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/blueprint/table.sql b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/blueprint/table.sql index 75353e23..7dc5a609 100644 --- a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/blueprint/table.sql +++ b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/blueprint/table.sql @@ -27,20 +27,6 @@ CREATE TABLE metaschema_modules_public.blueprint ( -- Lineage: where did this come from? template_id uuid DEFAULT NULL, - -- Execution state - status text NOT NULL DEFAULT 'draft' - CHECK (status IN ('draft', 'constructed', 'failed')), - - constructed_at timestamptz, - - error_details text, - - -- Output: mapping of ref names to created table IDs (populated after construct) - ref_map jsonb NOT NULL DEFAULT '{}', - - -- Snapshot of the definition at construct-time (immutable record of what was actually executed) - constructed_definition jsonb, - -- Content-addressable Merkle hashes (backend-computed via trigger) definition_hash uuid, @@ -56,7 +42,7 @@ CREATE TABLE metaschema_modules_public.blueprint ( ); COMMENT ON TABLE metaschema_modules_public.blueprint IS - 'An owned, executable blueprint scoped to a specific database. Created by copying from a blueprint_template via copy_template_to_blueprint() or built from scratch. The owner can customize the definition before executing it with construct_blueprint(). Each blueprint tracks its execution status (draft/constructed/failed) and stores the ref_map of created table IDs after construction.'; + 'An owned, editable blueprint scoped to a specific database. Created by copying from a blueprint_template via copy_template_to_blueprint() or built from scratch. The owner can customize the definition at any time. Execute it with construct_blueprint() which creates a separate blueprint_construction record to track the build.'; COMMENT ON COLUMN metaschema_modules_public.blueprint.id IS 'Unique identifier for this blueprint.'; @@ -77,26 +63,11 @@ COMMENT ON COLUMN metaschema_modules_public.blueprint.description IS 'Optional description of the blueprint.'; COMMENT ON COLUMN metaschema_modules_public.blueprint.definition IS - 'The blueprint definition as a JSONB document. Same format as blueprint_template.definition: contains tables[] (with nodes[], fields[], grants[], policies[] using $type) and relations[] (using $type). This is a mutable copy that the owner can customize before executing.'; + 'The blueprint definition as a JSONB document. Contains tables[] (each with table_name, optional schema_name, nodes[] for data behaviors, fields[], grants[], and policies[] using $type), relations[] (using $type with source_table/target_table and optional source_schema/target_schema), indexes[] (using table_name + column), and full_text_searches[] (using table_name + field + sources[]). Everything is name-based — no UUIDs in the definition.'; COMMENT ON COLUMN metaschema_modules_public.blueprint.template_id IS 'If this blueprint was created by copying a template, the ID of the source template. NULL if built from scratch.'; -COMMENT ON COLUMN metaschema_modules_public.blueprint.status IS - 'Execution state of the blueprint. draft: not yet executed (definition can still be modified). constructed: successfully executed via construct_blueprint(). failed: execution failed (see error_details). Defaults to draft.'; - -COMMENT ON COLUMN metaschema_modules_public.blueprint.constructed_at IS - 'Timestamp when construct_blueprint() successfully completed. NULL until constructed.'; - -COMMENT ON COLUMN metaschema_modules_public.blueprint.error_details IS - 'Error message from the most recent failed construct_blueprint() attempt. NULL unless status is failed.'; - -COMMENT ON COLUMN metaschema_modules_public.blueprint.ref_map IS - 'Mapping of ref names to created table UUIDs, populated by construct_blueprint() after successful execution. Format: {"products": "uuid", "categories": "uuid", ...}. Defaults to empty object.'; - -COMMENT ON COLUMN metaschema_modules_public.blueprint.constructed_definition IS - 'Immutable snapshot of the definition at construct-time. Preserved so the exact definition that was executed is recorded even if the user later modifies the definition for re-execution. NULL until constructed.'; - COMMENT ON COLUMN metaschema_modules_public.blueprint.created_at IS 'Timestamp when this blueprint was created.'; @@ -104,7 +75,7 @@ COMMENT ON COLUMN metaschema_modules_public.blueprint.definition_hash IS 'UUIDv5 Merkle root hash of the definition. Computed automatically via trigger from the ordered table_hashes. Used for content-addressable deduplication and provenance tracking. Backend-computed — clients should never set this directly.'; COMMENT ON COLUMN metaschema_modules_public.blueprint.table_hashes IS - 'JSONB map of table ref names to their individual UUIDv5 content hashes. Each table hash is computed from the canonical jsonb::text of the table entry. Enables structural comparison at the table level across blueprints and templates. Backend-computed via trigger.'; + 'JSONB map of table names to their individual UUIDv5 content hashes. Each table hash is computed from the canonical jsonb::text of the table entry. Enables structural comparison at the table level across blueprints and templates. Backend-computed via trigger.'; COMMENT ON COLUMN metaschema_modules_public.blueprint.updated_at IS 'Timestamp when this blueprint was last modified.'; @@ -113,7 +84,6 @@ COMMENT ON COLUMN metaschema_modules_public.blueprint.updated_at IS CREATE INDEX blueprint_owner_id_idx ON metaschema_modules_public.blueprint (owner_id); CREATE INDEX blueprint_database_id_idx ON metaschema_modules_public.blueprint (database_id); CREATE INDEX blueprint_template_id_idx ON metaschema_modules_public.blueprint (template_id); -CREATE INDEX blueprint_status_idx ON metaschema_modules_public.blueprint (status); CREATE INDEX blueprint_definition_hash_idx ON metaschema_modules_public.blueprint (definition_hash); COMMIT; diff --git a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql new file mode 100644 index 00000000..f28d49df --- /dev/null +++ b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql @@ -0,0 +1,84 @@ +-- Deploy schemas/metaschema_modules_public/tables/blueprint_construction/table to pg + +-- requires: schemas/metaschema_modules_public/schema +-- requires: schemas/metaschema_modules_public/tables/blueprint/table + +BEGIN; + +CREATE TABLE metaschema_modules_public.blueprint_construction ( + id uuid PRIMARY KEY DEFAULT uuidv7(), + + -- What was constructed + blueprint_id uuid NOT NULL, + + database_id uuid NOT NULL, + + -- The schema used as the default for tables without an explicit schema_name + schema_id uuid, + + -- Execution state + status text NOT NULL DEFAULT 'pending' + CHECK (status IN ('pending', 'constructing', 'constructed', 'failed')), + + error_details text, + + -- Output: mapping of table names to created table IDs (populated after construct) + table_map jsonb NOT NULL DEFAULT '{}', + + -- Snapshot of the definition at construct-time (immutable record of what was actually executed) + constructed_definition jsonb, + + constructed_at timestamptz, + + created_at timestamptz NOT NULL DEFAULT now(), + + updated_at timestamptz NOT NULL DEFAULT now(), + + CONSTRAINT blueprint_construction_blueprint_fkey + FOREIGN KEY (blueprint_id) REFERENCES metaschema_modules_public.blueprint(id) ON DELETE CASCADE, + CONSTRAINT blueprint_construction_db_fkey + FOREIGN KEY (database_id) REFERENCES metaschema_public.database(id) ON DELETE CASCADE +); + +COMMENT ON TABLE metaschema_modules_public.blueprint_construction IS + 'Tracks individual construction attempts of a blueprint. Each time construct_blueprint() is called, a new record is created here. This separates the editable blueprint definition from its build history, allowing blueprints to be re-executed, constructed into multiple databases, and maintain an audit trail of all construction attempts.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.id IS + 'Unique identifier for this construction attempt.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.blueprint_id IS + 'The blueprint that was constructed.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.database_id IS + 'The database the blueprint was constructed into.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.schema_id IS + 'The default schema used for tables that did not specify an explicit schema_name. NULL if not yet resolved.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.status IS + 'Execution state of this construction attempt. pending: created but not yet started. constructing: currently executing. constructed: successfully completed. failed: execution failed (see error_details).'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.error_details IS + 'Error message from a failed construction attempt. NULL unless status is failed.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.table_map IS + 'Mapping of table names to created table UUIDs, populated after successful construction. Format: {"products": "uuid", "categories": "uuid", ...}. Defaults to empty object.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.constructed_definition IS + 'Immutable snapshot of the definition at construct-time. Preserved so the exact definition that was executed is recorded even if the user later modifies the blueprint definition.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.constructed_at IS + 'Timestamp when construction successfully completed. NULL until constructed.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.created_at IS + 'Timestamp when this construction attempt was created.'; + +COMMENT ON COLUMN metaschema_modules_public.blueprint_construction.updated_at IS + 'Timestamp when this construction attempt was last modified.'; + + +CREATE INDEX blueprint_construction_blueprint_id_idx ON metaschema_modules_public.blueprint_construction (blueprint_id); +CREATE INDEX blueprint_construction_database_id_idx ON metaschema_modules_public.blueprint_construction (database_id); +CREATE INDEX blueprint_construction_status_idx ON metaschema_modules_public.blueprint_construction (status); + +COMMIT; 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 deleted file mode 100644 index 4b26695b..00000000 --- a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/field_module/table.sql +++ /dev/null @@ -1,39 +0,0 @@ --- Deploy schemas/metaschema_modules_public/tables/field_module/table to pg - --- requires: schemas/metaschema_modules_public/schema - -BEGIN; - -CREATE TABLE metaschema_modules_public.field_module ( - id uuid PRIMARY KEY DEFAULT uuidv7(), - database_id uuid NOT NULL, - - private_schema_id uuid NOT NULL DEFAULT uuid_nil(), - - 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[], - functions text[], - - -- - 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, - CONSTRAINT field_fkey FOREIGN KEY (field_id) REFERENCES metaschema_public.field (id) ON DELETE CASCADE, - CONSTRAINT private_schema_fkey FOREIGN KEY (private_schema_id) REFERENCES metaschema_public.schema (id) ON DELETE CASCADE -); - -CREATE INDEX field_module_database_id_idx ON metaschema_modules_public.field_module ( database_id ); -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/relation_provision/table.sql b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/relation_provision/table.sql index 3253fc60..890827b9 100644 --- a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/relation_provision/table.sql +++ b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/relation_provision/table.sql @@ -66,12 +66,10 @@ CREATE TABLE metaschema_modules_public.relation_provision ( expose_in_api boolean NOT NULL DEFAULT true, -- ========================================================================= - -- ManyToMany: field creation (forwarded to secure_table_provision) + -- ManyToMany: field creation (forwarded to provision_table) -- ========================================================================= - node_type text DEFAULT NULL, - - node_data jsonb NOT NULL DEFAULT '{}', + nodes jsonb NOT NULL DEFAULT '[]', -- ========================================================================= -- ManyToMany: grants (forwarded to secure_table_provision) @@ -139,7 +137,7 @@ COMMENT ON COLUMN metaschema_modules_public.relation_provision.database_id IS 'The database this relation belongs to. Required. Must match the database of both source_table_id and target_table_id.'; COMMENT ON COLUMN metaschema_modules_public.relation_provision.relation_type IS - 'The type of relation to create. Uses SuperCase naming matching the node_type_registry: + 'The type of relation to create. Uses SuperCase naming: - RelationBelongsTo: creates a FK field on source_table referencing target_table (e.g., tasks belongs to projects -> tasks.project_id). Field name auto-derived from target table. - RelationHasMany: creates a FK field on target_table referencing source_table (e.g., projects has many tasks -> tasks.project_id). Field name auto-derived from source table. Inverse of BelongsTo — same FK, different perspective. - RelationHasOne: creates a FK field + unique constraint on source_table referencing target_table (e.g., user_settings has one user -> user_settings.user_id with UNIQUE). Also supports shared-primary-key patterns (e.g., user_profiles.id = users.id) by setting field_name to the existing PK field. @@ -246,20 +244,11 @@ COMMENT ON COLUMN metaschema_modules_public.relation_provision.expose_in_api IS -- ManyToMany: field creation (forwarded to secure_table_provision) -- ============================================================================= -COMMENT ON COLUMN metaschema_modules_public.relation_provision.node_type IS - 'For RelationManyToMany: which generator to invoke for field creation on the junction table. Forwarded to secure_table_provision as-is. The trigger does not interpret or validate this value. - Examples: DataId (creates UUID primary key), DataDirectOwner (creates owner_id field), DataEntityMembership (creates entity_id field), DataOwnershipInEntity (creates both owner_id and entity_id), DataTimestamps, DataPeoplestamps, DataPublishable, DataSoftDelete. - NULL means no field creation beyond the FK fields (and composite key if use_composite_key is true). - Ignored for RelationBelongsTo/RelationHasOne.'; - -COMMENT ON COLUMN metaschema_modules_public.relation_provision.node_data IS - 'For RelationManyToMany: configuration passed to the generator function for field creation on the junction table. Forwarded to secure_table_provision as-is. The trigger does not interpret or validate this value. - Only used when node_type is set. Structure varies by node_type. Examples: - - DataId: {"field_name": "id"} (default field name is ''id'') - - DataEntityMembership: {"entity_field_name": "entity_id", "include_id": false, "include_user_fk": true} - - DataDirectOwner: {"owner_field_name": "owner_id"} - Defaults to ''{}'' (empty object). - Ignored for RelationBelongsTo/RelationHasOne.'; +COMMENT ON COLUMN metaschema_modules_public.relation_provision.nodes IS + 'For RelationManyToMany: array of node objects to apply to the junction table. Each element is a jsonb object with a required "$type" key and an optional "data" key. Forwarded to provision_table as-is. The trigger does not interpret or validate this value. + Examples: [{"$type": "DataId"}, {"$type": "DataTimestamps"}, {"$type": "DataDirectOwner", "data": {"owner_field_name": "author_id"}}]. + Defaults to ''[]'' (no node processing beyond the FK fields and composite key if use_composite_key is true). + Ignored for RelationBelongsTo/RelationHasOne/RelationHasMany.'; -- ============================================================================= -- ManyToMany: grants (forwarded to secure_table_provision) diff --git a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/secure_table_provision/table.sql b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/secure_table_provision/table.sql index b5a19e0b..cc13c1f8 100644 --- a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/secure_table_provision/table.sql +++ b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/secure_table_provision/table.sql @@ -15,12 +15,10 @@ CREATE TABLE metaschema_modules_public.secure_table_provision ( table_name text DEFAULT NULL, - node_type text DEFAULT NULL, + nodes jsonb NOT NULL DEFAULT '[]', use_rls boolean NOT NULL DEFAULT true, - node_data jsonb NOT NULL DEFAULT '{}', - fields jsonb[] NOT NULL DEFAULT '{}', grant_roles text[] NOT NULL DEFAULT ARRAY['authenticated'], @@ -47,7 +45,7 @@ CREATE TABLE metaschema_modules_public.secure_table_provision ( ); COMMENT ON TABLE metaschema_modules_public.secure_table_provision IS - 'Provisions security, fields, grants, and policies onto a table. Each row can independently: (1) create fields via node_type, (2) grant privileges via grant_privileges, (3) create RLS policies via policy_type. Multiple rows can target the same table to compose different concerns. All three concerns are optional and independent.'; + 'Provisions security, fields, grants, and policies onto a table. Each row can independently: (1) create fields via nodes[] array (supporting multiple Data* modules per row), (2) grant privileges via grant_privileges, (3) create RLS policies via policy_type. Multiple rows can target the same table to compose different concerns. All three concerns are optional and independent.'; COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.id IS 'Unique identifier for this provision row.'; @@ -64,14 +62,12 @@ COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.table_id IS COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.table_name IS 'Name of the target table. Used to create or look up the table when table_id is not provided. If omitted, it is backfilled from the resolved table.'; -COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.node_type IS - 'Which generator to invoke for field creation. One of: DataId, DataDirectOwner, DataEntityMembership, DataOwnershipInEntity, DataTimestamps, DataPeoplestamps, DataPublishable, DataSoftDelete. NULL means no field creation — the row only provisions grants and/or policies.'; +COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.nodes IS + 'Array of node objects to apply to the table. Each element is a jsonb object with a required "$type" key (one of: DataId, DataDirectOwner, DataEntityMembership, DataOwnershipInEntity, DataTimestamps, DataPeoplestamps, DataPublishable, DataSoftDelete, DataEmbedding, DataFullTextSearch, DataSlug, etc.) and an optional "data" key containing generator-specific configuration. Supports multiple nodes per row, matching the blueprint definition format. Example: [{"$type": "DataId"}, {"$type": "DataTimestamps"}, {"$type": "DataDirectOwner", "data": {"owner_field_name": "author_id"}}]. Defaults to ''[]'' (no node processing).'; COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.use_rls IS 'If true and Row Level Security is not yet enabled on the target table, enable it. Automatically set to true by the trigger when policy_type is provided. Defaults to true.'; -COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.node_data IS - 'Configuration passed to the generator function for field creation (only used when node_type is set). Known keys include: field_name (text, default ''id'') for DataId, owner_field_name (text, default ''owner_id'') for DataDirectOwner/DataOwnershipInEntity, entity_field_name (text, default ''entity_id'') for DataEntityMembership/DataOwnershipInEntity, include_id (boolean, default true) for most node_types, include_user_fk (boolean, default true) to add FK to users table, create_index (boolean, default true) to create btree indexes on FK fields for join and cascade performance. Defaults to ''{}''.'; COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.fields IS 'PostgreSQL array of jsonb field definition objects to create on the target table. Each object has keys: "name" (text, required), "type" (text, required), "default" (text, optional), "is_required" (boolean, optional, defaults to false), "min" (float, optional), "max" (float, optional), "regexp" (text, optional), "index" (boolean, optional, defaults to false — creates a btree index on the field). min/max generate CHECK constraints: for text/citext they constrain character_length, for integer/float types they constrain the value. regexp generates a CHECK (col ~ pattern) constraint for text/citext. Fields are created via metaschema.create_field() after any node_type generator runs, and their IDs are appended to out_fields. Example: ARRAY[''{"name":"username","type":"citext","max":256,"regexp":"^[a-z0-9_]+$"}''::jsonb, ''{"name":"score","type":"integer","min":0,"max":100}''::jsonb]. Defaults to ''{}'' (no additional fields).'; @@ -101,11 +97,10 @@ COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.policy_data I 'Opaque configuration passed through to metaschema.create_policy(). Structure varies by policy_type and is not interpreted by this trigger. Defaults to ''{}''.'; COMMENT ON COLUMN metaschema_modules_public.secure_table_provision.out_fields IS - 'Output column populated by the trigger after field creation. Contains the UUIDs of the metaschema fields created on the target table by this provision row''s generator. NULL when node_type is NULL or before the trigger runs. Callers should not set this directly.'; + 'Output column populated by the trigger after field creation. Contains the UUIDs of the metaschema fields created on the target table by this provision row''s nodes. NULL when nodes is empty or before the trigger runs. Callers should not set this directly.'; CREATE INDEX secure_table_provision_database_id_idx ON metaschema_modules_public.secure_table_provision ( database_id ); CREATE INDEX secure_table_provision_table_id_idx ON metaschema_modules_public.secure_table_provision ( table_id ); -CREATE INDEX secure_table_provision_node_type_idx ON metaschema_modules_public.secure_table_provision ( node_type ); COMMIT; diff --git a/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/storage_module/table.sql b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/storage_module/table.sql new file mode 100644 index 00000000..e5b057da --- /dev/null +++ b/packages/metaschema-modules/deploy/schemas/metaschema_modules_public/tables/storage_module/table.sql @@ -0,0 +1,47 @@ +-- Deploy schemas/metaschema_modules_public/tables/storage_module/table to pg + +-- requires: schemas/metaschema_modules_public/schema + +BEGIN; + +CREATE TABLE metaschema_modules_public.storage_module ( + id uuid PRIMARY KEY DEFAULT uuidv7(), + database_id uuid NOT NULL, + + -- Schema references + schema_id uuid NOT NULL DEFAULT uuid_nil(), + private_schema_id uuid NOT NULL DEFAULT uuid_nil(), + + -- Generated table IDs (populated by the generator) + buckets_table_id uuid NOT NULL DEFAULT uuid_nil(), + files_table_id uuid NOT NULL DEFAULT uuid_nil(), + upload_requests_table_id uuid NOT NULL DEFAULT uuid_nil(), + + -- Table names (input to the generator) + buckets_table_name text NOT NULL DEFAULT 'buckets', + files_table_name text NOT NULL DEFAULT 'files', + upload_requests_table_name text NOT NULL DEFAULT 'upload_requests', + + -- Entity table for RLS (users table, since users and orgs share it) + entity_table_id uuid NULL, + + -- Per-database configurable settings (NULL = use plugin defaults) + upload_url_expiry_seconds integer NULL, -- Presigned PUT URL expiry (default: 900 = 15 min) + download_url_expiry_seconds integer NULL, -- Presigned GET URL expiry (default: 3600 = 1 hour) + default_max_file_size bigint NULL, -- Global max file size in bytes (default: 200MB). Bucket-level overrides this. + max_filename_length integer NULL, -- Max filename length in chars (default: 1024) + cache_ttl_seconds integer NULL, -- LRU cache TTL for this config (default: 300 dev / 3600 prod) + + -- Constraints + 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, + CONSTRAINT private_schema_fkey FOREIGN KEY (private_schema_id) REFERENCES metaschema_public.schema (id) ON DELETE CASCADE, + CONSTRAINT buckets_table_fkey FOREIGN KEY (buckets_table_id) REFERENCES metaschema_public.table (id) ON DELETE CASCADE, + CONSTRAINT files_table_fkey FOREIGN KEY (files_table_id) REFERENCES metaschema_public.table (id) ON DELETE CASCADE, + CONSTRAINT upload_requests_table_fkey FOREIGN KEY (upload_requests_table_id) REFERENCES metaschema_public.table (id) ON DELETE CASCADE, + CONSTRAINT entity_table_fkey FOREIGN KEY (entity_table_id) REFERENCES metaschema_public.table (id) ON DELETE CASCADE +); + +CREATE INDEX storage_module_database_id_idx ON metaschema_modules_public.storage_module ( database_id ); + +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 2ef7d10b..ef126735 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,7 +16,7 @@ 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 (e.g., 'TableUserProfiles', 'TableOrganizationSettings', 'TableUserSettings') node_type text NOT NULL, -- Type-specific parameters as jsonb diff --git a/packages/metaschema-modules/pgpm.plan b/packages/metaschema-modules/pgpm.plan index 01c0f958..4059b36d 100644 --- a/packages/metaschema-modules/pgpm.plan +++ b/packages/metaschema-modules/pgpm.plan @@ -13,7 +13,6 @@ schemas/metaschema_modules_public/tables/default_ids_module/table [schemas/metas schemas/metaschema_modules_public/tables/denormalized_table_field/table [schemas/metaschema_modules_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_modules_public/tables/denormalized_table_field/table schemas/metaschema_modules_public/tables/emails_module/table [schemas/metaschema_modules_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_modules_public/tables/emails_module/table schemas/metaschema_modules_public/tables/encrypted_secrets_module/table [schemas/metaschema_modules_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_modules_public/tables/encrypted_secrets_module/table -schemas/metaschema_modules_public/tables/field_module/table [schemas/metaschema_modules_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_modules_public/tables/field_module/table schemas/metaschema_modules_public/tables/invites_module/table [schemas/metaschema_modules_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_modules_public/tables/invites_module/table schemas/metaschema_modules_public/tables/levels_module/table [schemas/metaschema_modules_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_modules_public/tables/levels_module/table schemas/metaschema_modules_public/tables/limits_module/table [schemas/metaschema_modules_public/schema] 2017-08-11T08:11:51Z skitch # add schemas/metaschema_modules_public/tables/limits_module/table @@ -35,3 +34,5 @@ schemas/metaschema_modules_public/tables/secure_table_provision/table [schemas/m schemas/metaschema_modules_public/tables/relation_provision/table [schemas/metaschema_modules_public/schema] 2026-02-26T00:00:00Z Constructive # add schemas/metaschema_modules_public/tables/relation_provision/table schemas/metaschema_modules_public/tables/blueprint_template/table [schemas/metaschema_modules_public/schema] 2026-03-20T00:00:00Z Constructive # add blueprint_template table for shareable schema recipes schemas/metaschema_modules_public/tables/blueprint/table [schemas/metaschema_modules_public/schema schemas/metaschema_modules_public/tables/blueprint_template/table] 2026-03-20T00:00:01Z Constructive # add blueprint table for owned executable blueprints +schemas/metaschema_modules_public/tables/blueprint_construction/table [schemas/metaschema_modules_public/schema schemas/metaschema_modules_public/tables/blueprint/table] 2026-03-31T00:00:00Z Constructive # add blueprint_construction table for construction state tracking +schemas/metaschema_modules_public/tables/storage_module/table [schemas/metaschema_modules_public/schema] 2026-03-24T00:00:00Z devin # add storage_module config table for files and buckets diff --git a/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql b/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql new file mode 100644 index 00000000..f920d1a5 --- /dev/null +++ b/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql @@ -0,0 +1,3 @@ +-- Revert schemas/metaschema_modules_public/tables/blueprint_construction/table + +DROP TABLE IF EXISTS metaschema_modules_public.blueprint_construction; diff --git a/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/field_module/table.sql b/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/field_module/table.sql deleted file mode 100644 index 27924803..00000000 --- a/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/field_module/table.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert schemas/metaschema_modules_public/tables/field_module/table from pg - -BEGIN; - -DROP TABLE IF EXISTS metaschema_modules_public.field_module; - -COMMIT; diff --git a/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/storage_module/table.sql b/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/storage_module/table.sql new file mode 100644 index 00000000..e0b6d0f3 --- /dev/null +++ b/packages/metaschema-modules/revert/schemas/metaschema_modules_public/tables/storage_module/table.sql @@ -0,0 +1,7 @@ +-- Revert schemas/metaschema_modules_public/tables/storage_module/table from pg + +BEGIN; + +DROP TABLE metaschema_modules_public.storage_module; + +COMMIT; diff --git a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/blueprint/table.sql b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/blueprint/table.sql index 7a71abb5..d5683b39 100644 --- a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/blueprint/table.sql +++ b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/blueprint/table.sql @@ -11,11 +11,6 @@ SELECT description, definition, template_id, - status, - constructed_at, - error_details, - ref_map, - constructed_definition, definition_hash, table_hashes, created_at, diff --git a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql new file mode 100644 index 00000000..aceac402 --- /dev/null +++ b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/blueprint_construction/table.sql @@ -0,0 +1,10 @@ +-- Verify schemas/metaschema_modules_public/tables/blueprint_construction/table + +BEGIN; + +SELECT id, blueprint_id, database_id, schema_id, status, error_details, + table_map, constructed_definition, constructed_at, created_at, updated_at +FROM metaschema_modules_public.blueprint_construction +WHERE FALSE; + +ROLLBACK; diff --git a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/field_module/table.sql b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/field_module/table.sql deleted file mode 100644 index 94cb11ed..00000000 --- a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/field_module/table.sql +++ /dev/null @@ -1,9 +0,0 @@ --- Verify schemas/metaschema_modules_public/tables/field_module/table on pg - -BEGIN; - -SELECT id, database_id, private_schema_id, table_id, field_id, node_type, data, triggers, functions -FROM metaschema_modules_public.field_module -WHERE FALSE; - -ROLLBACK; diff --git a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/relation_provision/table.sql b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/relation_provision/table.sql index 71122185..8782782b 100644 --- a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/relation_provision/table.sql +++ b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/relation_provision/table.sql @@ -18,14 +18,16 @@ SELECT source_field_name, target_field_name, use_composite_key, - node_type, - node_data, + create_index, + expose_in_api, + nodes, grant_roles, grant_privileges, policy_type, policy_privileges, policy_role, policy_permissive, + policy_name, policy_data, out_field_id, out_junction_table_id, diff --git a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/secure_table_provision/table.sql b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/secure_table_provision/table.sql index b9a983ee..8b8df183 100644 --- a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/secure_table_provision/table.sql +++ b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/secure_table_provision/table.sql @@ -8,9 +8,8 @@ SELECT schema_id, table_id, table_name, - node_type, + nodes, use_rls, - node_data, fields, grant_roles, grant_privileges, diff --git a/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/storage_module/table.sql b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/storage_module/table.sql new file mode 100644 index 00000000..47099c40 --- /dev/null +++ b/packages/metaschema-modules/verify/schemas/metaschema_modules_public/tables/storage_module/table.sql @@ -0,0 +1,7 @@ +-- Verify schemas/metaschema_modules_public/tables/storage_module/table on pg + +BEGIN; + +SELECT verify_table ('metaschema_modules_public.storage_module'); + +ROLLBACK;