Type Fix Explanation

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

🔧 Type Compatibility Fix: ParameterMetadata vs UiInputDefinition

Issue: Type mismatch between smart properties and embeddable inputs
Status: ✅ Fixed
Date: December 10, 2025


🚨 The Problem

Original Error

Type 'UiInputDefinition' is not assignable to type 'ParameterMetadata'.
  Types of property 'type' are incompatible.
    Type 'EmbeddableInputType' is not assignable to type 'ParameterType'.
      Type '"color"' is not assignable to type 'ParameterType'.

Root Cause

We had two different type systems for properties:

// Type System 1: EmbeddableInputType (UI-focused)
// Location: @libs-foundational/core-models
export type EmbeddableInputType =
  | 'string'
  | 'number'
  | 'boolean'
  | 'select'  // ❌ UI-specific
  | 'color'   // ❌ UI-specific
  | 'icon'    // ❌ UI-specific
  | 'image'   // ❌ UI-specific
  | 'json'
  | 'array'
  | 'object';

// Type System 2: ParameterType (Module-focused)
// Location: @libs-core/shared-models
export type ParameterType =
  | 'string'
  | 'number'
  | 'boolean'
  | 'date'    // ✅ Module-specific
  | 'object'
  | 'array'
  | 'enum'    // ✅ Canonical for select
  | 'any';

The Conflict:


✅ The Solution

Change 1: Smart Properties Now Return ParameterMetadata

Before (returned UiInputDefinition):

export function createSelectProperty(config): UiInputDefinition {
  return {
    name: config.name,
    type: 'select', // ❌ Not compatible with ParameterType
    label: config.label,
    // ...
  };
}

After (returns ParameterMetadata):

export function createSelectProperty(config): ParameterMetadata {
  return {
    name: config.name,
    type: 'enum',  // ✅ Matches ParameterType
    required: false,
    label: config.label,
    // ...
  } as ParameterMetadata;
}

Key Changes:

Change 2: Updated All Smart Property Factories

// SmartButtonProperties
export const SmartButtonProperties = {
  variant: (overrides: Partial<ParameterMetadata> = {}) => ({
    ...createSelectProperty({...}),
    ...overrides  // Now spreads ParameterMetadata overrides
  }),
  
  size: (overrides: Partial<ParameterMetadata> = {}) => ({
    ...createSelectProperty({...}),
    ...overrides
  }),
  // ...
};

// SmartGlobalProperties
export const SmartGlobalProperties = {
  disabled: (overrides: Partial<ParameterMetadata> = {}) => ({
    ...createBooleanProperty({...}),
    ...overrides
  }),
  
  loading: (overrides: Partial<ParameterMetadata> = {}) => ({
    ...createBooleanProperty({...}),
    ...overrides
  }),
};

Change 3: Updated Embeddable Imports

// Before
import { UiInputDefinition } from '@libs-foundational/core-models';

// After
import { ParameterMetadata } from '@libs-core/shared-models';

🎯 Why ParameterMetadata?

ParameterMetadata is the Canonical Type

Reasons:

  1. Module System Standard: Used by BrowserModuleContract and ModuleEmbeddableComponent
  2. Richer Interface: Includes validation, UI hints, examples
  3. Backend Aligned: Maps cleanly to MongoDB property schema
  4. AI-Ready: Supports aiContext metadata (future)
  5. Cross-System: Works for both embeddables AND module actions

Type Mapping: UI → Module

UI Type (EmbeddableInputType) Module Type (ParameterType) Notes
'string' 'string' ✅ Direct match
'number' 'number' ✅ Direct match
'boolean' 'boolean' ✅ Direct match
'select' 'enum' ⚠️ Semantic mapping
'color' 'string' ⚠️ String representation (hex/rgb)
'icon' 'string' ⚠️ Icon name as string
'image' 'string' ⚠️ URL/path as string
'json' 'object' ⚠️ Parsed object
'array' 'array' ✅ Direct match
'object' 'object' ✅ Direct match

UI-specific types ('select', 'color', 'icon') are UI hints, not fundamental data types. They map to canonical types with additional metadata.


🔍 How Smart Properties Connect to Backend

Current Architecture

┌─────────────────────────────────────────────────────────┐
│ Backend Property (MongoDB)                               │
│ {                                                        │
│   id: "custom_buttonVariant",                           │
│   controlType: "select",  ← UI hint                     │
│   validation: {                                          │
│     options: [                                           │
│       { value: 'primary', label: 'Primary' }            │
│     ]                                                    │
│   }                                                      │
│ }                                                        │
└─────────────────────────────────────────────────────────┘
                        ↓ Aligned with
┌─────────────────────────────────────────────────────────┐
│ Frontend Smart Property                                  │
│ SmartButtonProperties.variant() {                       │
│   name: 'variant',                                       │
│   type: 'enum',  ← Maps to controlType: "select"       │
│   options: [     ← Same as backend                      │
│     { value: 'primary', label: 'Primary' }             │
│   ],                                                     │
│   transform: mapButtonVariant  ← CSS mapper             │
│ }                                                        │
└─────────────────────────────────────────────────────────┘

Key Points:


📊 Impact of the Fix

Before Fix

// Embeddable definition
export const BUTTON_EMBEDDABLE = {
  inputs: [
    SmartButtonProperties.variant({ order: 1 }),
    //                     ↑
    //          Returns UiInputDefinition
    //          ❌ Type mismatch error
  ]
};

Error: Type 'UiInputDefinition' is not assignable to type 'ParameterMetadata'

After Fix

// Embeddable definition
export const BUTTON_EMBEDDABLE = {
  inputs: [
    SmartButtonProperties.variant({ order: 1 }),
    //                     ↑
    //          Returns ParameterMetadata
    //          ✅ Type compatible
  ]
};

Result: No errors, full type safety!


🎨 Code Cleanup: Removed V1/V2 Confusion

Before (Confusing)

embeddables/embeddable-schemas/src/lib/ui-core/
├── button.embeddable.ts      ← V1 (inline definitions)
└── button.embeddable.v2.ts   ← V2 (property references)

Problem: Two versions create confusion - which one is correct?

After (Clear)

embeddables/embeddable-schemas/src/lib/ui-core/
└── button.embeddable.ts      ← Single source with commented history

Solution: Keep ONE file with old approach commented for reference:

export const BUTTON_EMBEDDABLE = {
  inputs: [
    // ✅ Current: Smart properties with mappers
    SmartButtonProperties.variant({ order: 1 }),
    
    /* OLD APPROACH (Hardcoded - commented for reference):
    {
      name: 'variant',
      type: 'string',
      options: [...]
    }
    */
  ]
};

Benefits:


🚀 Next Steps

Immediate

Short Term (Next Sessions)

Medium Term


✅ Verification

Type Check

# Should compile without errors
npx tsc --noEmit

Serve Process

# Should show no type errors
yarn serve:gograb-space

Expected Result

✓ Compiled successfully
No type errors found

Status: ✅ Fixed
Confidence: 100%
Impact: Type safety restored, codebase simplified
Technical Debt: Removed (V1/V2 confusion eliminated)