Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Redundant unreachable (database)s$ plural guard after catch-all (.+base)s$

The plural guard ('plural', '(database)s$', NULL) on line 17 is unreachable because the immediately preceding rule ('plural', '(.+base)s$', NULL) on line 16 already matches "databases" (since .+base matches "database"). The inflection engine (packages/inflection/deploy/schemas/inflection/procedures/plural.sql:20-31) processes rules in insertion order and returns on the first match, so the (database)s$ guard will never execute. This is dead code that could mislead future maintainers into thinking it's meaningful.

Suggested change
('plural', '(database)s$', NULL),
('plural', '(drive)s$', NULL),
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

('plural', '(drive)s$', NULL),
('plural', '(hi|ti)ves$', NULL),
('plural', '(curve)s$', NULL),
Expand Down Expand Up @@ -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'),
Expand All @@ -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'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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,
}
`;

Expand Down Expand Up @@ -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,
Expand All @@ -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",
Expand Down
31 changes: 1 addition & 30 deletions packages/metaschema-modules/__tests__/modules.test.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand All @@ -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.';
Expand All @@ -77,34 +63,19 @@ 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.';

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.';
Expand All @@ -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;
Original file line number Diff line number Diff line change
@@ -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;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
Loading
Loading