Editor Module Contract Migration
Documentation for Plans
๐ EditorModuleContract โ BrowserModuleContract Migration Guide
Date: October 31, 2025
Time: Session 1
Status: ๐ MIGRATION GUIDE
Purpose: Guide developers through migrating from EditorModuleContract to BrowserModuleContract
๐ฏ Why Migrate?
โ Old Pattern (EditorModuleContract)
- Basic metadata
- Limited parameter information
- No AI trainability
- Manual UI generation
- Scattered validation logic
โ New Pattern (BrowserModuleContract)
- Rich metadata with ModuleParameterBuilder
- AI-trainable parameters with examples and validation
- Automatic UI generation from parameter metadata
- Unified parameter system across actions, components, validation
- Better TypeScript inference and safety
- Golden Template for consistency
๐ Migration Checklist
- Replace imports from
@libs-foundational/core-angularto@libs-core/module-base - Change interface from
EditorModuleContracttoBrowserModuleContract - Update manifest from
EditorModuleManifesttoModuleManifest - Add
@ModuleDecoratorto class - Convert public actions to use
ModuleParameterBuilder - Update routes to use
loadComponent(lazy loading) - Add rich metadata to embeddable components
- Update configuration structure
- Add tags for discoverability
- Update lifecycle methods to match new signature
- Test thoroughly
๐ง Step-by-Step Migration
Step 1: Update Imports
Before:
import {
EditorModuleContract,
ModuleConfig,
Module
} from '@libs-foundational/core-angular';
import type { EditorModuleManifest } from '@libs-foundational/core-angular';
After:
import {
BrowserModuleContract,
ModuleManifest,
ModuleDecorator,
ModuleParameterBuilder
} from '@libs-core/module-base';
Step 2: Update Class Declaration
Before:
@Module({
manifest: MY_MODULE_MANIFEST
})
@Injectable({
providedIn: 'root'
})
export class MyModule implements EditorModuleContract {
readonly manifest = MY_MODULE_MANIFEST;
After:
@ModuleDecorator({
id: 'my-module',
version: '1.0.0',
name: 'My Module'
})
@Injectable({
providedIn: 'root'
})
export class MyModule implements BrowserModuleContract {
readonly manifest: ModuleManifest = {
// Inline manifest (preferred) or external
};
Step 3: Convert Public Actions
Before:
publicActions: [
{
id: 'create-item',
actionClass: CreateItemAction,
displayName: 'Create Item',
description: 'Create a new item',
parameters: [
{
name: 'name',
type: 'string',
description: 'Item name',
required: true
}
],
returnType: 'Promise<Item>'
}
]
After:
publicActions: [
{
id: 'items:create',
displayName: 'Create Item',
description: 'Create a new item with validation',
category: 'crud',
parameters: [
ModuleParameterBuilder.string('name')
.required('Name is required')
.minLength(2, 'Name must be at least 2 characters')
.maxLength(100, 'Name must be less than 100 characters')
.label('Item Name')
.placeholder('Enter item name')
.example('My Awesome Item')
.build()
],
handler: (params) => of(this.createItem(params))
}
]
Benefits:
- โ Rich validation metadata
- โ UI hints (label, placeholder)
- โ AI-understandable examples
- โ Observable handler (consistent async)
- โ Category for organization
Step 4: Update Routes
Before:
routes: [
{
path: 'items',
component: ItemListComponent,
data: {
title: 'Items',
icon: 'list'
}
}
]
After:
routes: [
{
path: 'items',
loadComponent: () => import('./components/item-list/item-list.component')
.then(m => m.ItemListComponent),
data: {
title: 'Items',
icon: 'fas fa-list',
description: 'Manage all items'
}
}
]
Benefits:
- โ Lazy loading (better performance)
- โ Smaller initial bundle
- โ Better code splitting
Step 5: Enhance Embeddable Components
Before:
embeddableComponents: [
{
id: 'item-list-widget',
componentType: ItemListWidgetComponent,
displayName: 'Item List',
description: 'Displays items',
category: 'widget',
inputs: [
{
name: 'maxItems',
type: 'number',
required: false,
defaultValue: 10
}
],
usesThemeSystem: true,
usesStorage: true
}
]
After:
embeddableComponents: [
{
id: 'item-list-widget',
displayName: 'Item List Widget',
description: 'Displays a compact list of items for dashboards',
category: 'data-display',
componentType: undefined as any, // Will be set when component is created
inputs: [
ModuleParameterBuilder.number('maxItems')
.min(1, 'Must show at least 1 item')
.max(100, 'Cannot show more than 100 items')
.defaultValue(10)
.label('Maximum Items')
.description('Number of items to display in the widget')
.build(),
ModuleParameterBuilder.boolean('showActions')
.defaultValue(true)
.label('Show Action Buttons')
.description('Display action buttons for each item')
.build()
]
}
]
Benefits:
- โ Rich parameter validation
- โ Better UI generation
- โ More descriptive metadata
Step 6: Update Configuration
Before:
// Usually scattered or missing
After:
configuration: {
defaults: {
itemsPerPage: 20,
sortOrder: 'desc',
enableNotifications: true
}
}
Step 7: Add Tags
Before:
// Not available in old pattern
After:
tags: ['items', 'inventory', 'crud', 'business']
Benefits:
- โ Better discoverability
- โ Category filtering
- โ Search functionality
Step 8: Update Lifecycle Methods
The lifecycle methods remain similar, but the new pattern uses generic types:
Before:
configure(config: ModuleConfig): Observable<void> {
this.storage = config.storage;
this.theme = config.theme;
return of(void 0);
}
After:
configure<T = Record<string, unknown>>(config: T): Observable<void> {
this.config = config;
this.storage = (config as any).storage;
this.theme = (config as any).theme;
console.log('๐๏ธ [Lambo Builder - My Module] Configuring module');
return of(void 0);
}
๐ Complete Example
See the Golden Template for a complete, working example:
/modules/user-management/src/lib/user-management.module.ts
Or read the documentation:
docs/english/GoGrab-Space/plans/lambo-builder/golden-template.md
๐งช Testing After Migration
Build the module:
cd /Users/fguijarro/developer/repos/editor npx nx build your-module-nameCheck for linter errors:
npx nx lint your-module-name --fixRun tests:
npx nx test your-module-nameTest in the app:
- Start the development server
- Navigate to module routes
- Test public actions
- Verify embeddable components
- Check lifecycle (configure, initialize, start, stop, destroy)
๐ฏ Benefits Summary
| Feature | EditorModuleContract | BrowserModuleContract |
|---|---|---|
| Parameter Metadata | Basic | Rich with validation |
| UI Generation | Manual | Automatic from metadata |
| AI Trainability | Limited | Excellent |
| TypeScript Safety | Good | Excellent |
| Lazy Loading | Optional | Built-in |
| Unified Parameters | No | Yes |
| Examples | No | Yes |
| Categories | No | Yes |
| Tags | No | Yes |
| Configuration | Scattered | Centralized |
๐จ Common Issues
Issue 1: Import Errors
Problem: Can't find @libs-core/module-base
Solution: Check tsconfig.base.json for path mapping:
{
"compilerOptions": {
"paths": {
"@libs-core/module-base": ["libs/core/module-base/src/index.ts"]
}
}
}
Issue 2: Observable vs Promise
Problem: Old code used Promises for actions
Solution: Wrap in of():
handler: (params) => of(this.myMethod(params))
Issue 3: Component Type
Problem: ComponentType is undefined as any
Solution: This is intentional. The component will be set when the module is loaded. For now, use undefined as any to satisfy TypeScript.
๐ Getting Help
- Check the Golden Template - Most answers are there
- Read the Architecture Docs -
/docs/english/GoGrab-Space/plans/lambo-builder/ - Look at existing migrations - User Management module is complete
- Ask the team - We're building the future together!
๐ Completion Criteria
Your migration is complete when:
- All imports updated
- BrowserModuleContract implemented
- ModuleParameterBuilder used for all parameters
- Routes use lazy loading
- Rich metadata for embeddable components
- Configuration centralized
- Tags added
- No linter errors
- All tests pass
- Module works in the app
- Documentation updated
Let's build the most incredible module system in the universe! ๐