mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 22:34:56 +00:00
583 lines
12 KiB
Markdown
583 lines
12 KiB
Markdown
# MetaBuilder Data-Driven Architecture
|
|
|
|
## Overview
|
|
|
|
MetaBuilder is designed to minimize TypeScript dependencies by making everything procedurally generated from JSON configurations and Lua scripts. This document explains the architecture and how to extend it.
|
|
|
|
## Core Principle
|
|
|
|
**Code should only exist for infrastructure. Content is data.**
|
|
|
|
```
|
|
Infrastructure Layer (TypeScript):
|
|
- App.tsx
|
|
- GenericPage.tsx
|
|
- RenderComponent.tsx
|
|
- Database.ts
|
|
- LuaEngine.ts
|
|
- Shadcn components
|
|
|
|
Content Layer (JSON + Lua):
|
|
- Pages
|
|
- Components
|
|
- Workflows
|
|
- Scripts
|
|
- Users
|
|
- Permissions
|
|
```
|
|
|
|
## Architecture Layers
|
|
|
|
### Layer 1: Data Storage (`/src/seed-data/`)
|
|
|
|
Modular seed data modules that initialize the database:
|
|
|
|
```typescript
|
|
/src/seed-data/
|
|
├── index.ts // Orchestrates initialization
|
|
├── users.ts // User accounts & auth
|
|
├── components.ts // Component configurations
|
|
├── scripts.ts // Lua script library
|
|
├── workflows.ts // Process definitions
|
|
├── pages.ts // Page structures
|
|
└── packages.ts // Package system
|
|
```
|
|
|
|
**Each module:**
|
|
- Checks if data already exists
|
|
- Returns early if already initialized
|
|
- Adds data to database via Database API
|
|
- Can be selectively enabled/disabled
|
|
|
|
### Layer 2: Data Persistence (`/src/lib/database.ts`)
|
|
|
|
The `Database` class provides a unified KV-store interface:
|
|
|
|
```typescript
|
|
// Users
|
|
await Database.addUser(user)
|
|
await Database.getUsers()
|
|
await Database.updateUser(id, updates)
|
|
|
|
// Components
|
|
await Database.addComponentConfig(config)
|
|
await Database.getComponentConfigs()
|
|
|
|
// Pages
|
|
await Database.addPage(page)
|
|
await Database.getPages()
|
|
|
|
// Scripts
|
|
await Database.addLuaScript(script)
|
|
await Database.getLuaScripts()
|
|
|
|
// Workflows
|
|
await Database.addWorkflow(workflow)
|
|
await Database.getWorkflows()
|
|
```
|
|
|
|
### Layer 3: Logic Layer (Lua)
|
|
|
|
All business logic is written in Lua and stored as data:
|
|
|
|
```lua
|
|
-- scripts.ts
|
|
{
|
|
id: 'script_welcome_message',
|
|
name: 'Welcome Message Generator',
|
|
code: `
|
|
function generateWelcome(username)
|
|
return "Welcome back, " .. username .. "!"
|
|
end
|
|
return generateWelcome
|
|
`,
|
|
parameters: [{ name: 'username', type: 'string' }],
|
|
returnType: 'string'
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Scripts stored in database
|
|
- Can be edited via UI
|
|
- Sandboxed execution
|
|
- No recompilation needed
|
|
|
|
### Layer 4: Workflow Layer
|
|
|
|
Workflows define multi-step processes as node graphs:
|
|
|
|
```typescript
|
|
{
|
|
id: 'workflow_user_registration',
|
|
name: 'User Registration Flow',
|
|
nodes: [
|
|
{
|
|
id: 'validate',
|
|
type: 'condition',
|
|
label: 'Validate Input',
|
|
config: { luaScriptId: 'script_validate_email' }
|
|
},
|
|
{
|
|
id: 'create',
|
|
type: 'action',
|
|
label: 'Create User',
|
|
config: { action: 'create_user' }
|
|
}
|
|
],
|
|
edges: [
|
|
{ source: 'validate', target: 'create', label: 'valid' }
|
|
]
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Visual workflow editor possible
|
|
- Complex logic without code
|
|
- Reusable node types
|
|
- Clear process flow
|
|
|
|
### Layer 5: Page Layer
|
|
|
|
Pages combine components, scripts, and workflows:
|
|
|
|
```typescript
|
|
{
|
|
id: 'page_dashboard',
|
|
title: 'User Dashboard',
|
|
level: 2,
|
|
layout: 'dashboard',
|
|
components: [/* component tree */],
|
|
luaScripts: {
|
|
onLoad: 'script_page_analytics',
|
|
onUnload: 'script_save_state'
|
|
},
|
|
permissions: {
|
|
requiresAuth: true,
|
|
customCheck: 'script_permission_check'
|
|
}
|
|
}
|
|
```
|
|
|
|
**Rendered by:**
|
|
```tsx
|
|
<GenericPage
|
|
pageId="page_dashboard"
|
|
user={currentUser}
|
|
level={2}
|
|
onNavigate={handleNavigate}
|
|
/>
|
|
```
|
|
|
|
### Layer 6: Presentation Layer
|
|
|
|
Generic TSX components render from definitions:
|
|
|
|
```tsx
|
|
// GenericPage.tsx
|
|
// Reads PageDefinition from database
|
|
// Renders layout based on page.layout
|
|
// Executes onLoad scripts
|
|
// Renders component tree via RenderComponent
|
|
|
|
// RenderComponent.tsx
|
|
// Recursive component renderer
|
|
// Maps type string to actual component
|
|
// Passes props from configuration
|
|
// Handles children recursively
|
|
```
|
|
|
|
## Data Flow
|
|
|
|
### Page Load Flow
|
|
|
|
```
|
|
1. User navigates to page
|
|
↓
|
|
2. App.tsx determines level
|
|
↓
|
|
3. GenericPage loads PageDefinition from Database
|
|
↓
|
|
4. Permission check (role + custom Lua)
|
|
↓
|
|
5. Execute onLoad Lua script
|
|
↓
|
|
6. Render layout (default/sidebar/dashboard/blank)
|
|
↓
|
|
7. RenderComponent processes component tree
|
|
↓
|
|
8. Components render (shadcn + custom)
|
|
↓
|
|
9. User interacts
|
|
↓
|
|
10. Event handlers execute (Lua scripts)
|
|
↓
|
|
11. User leaves
|
|
↓
|
|
12. Execute onUnload Lua script
|
|
```
|
|
|
|
### Component Rendering Flow
|
|
|
|
```
|
|
ComponentInstance (JSON)
|
|
↓
|
|
RenderComponent.tsx
|
|
↓
|
|
Type Lookup (Registry)
|
|
↓
|
|
Shadcn Component / Custom Component
|
|
↓
|
|
Props Applied
|
|
↓
|
|
Children Recursively Rendered
|
|
↓
|
|
Events Bound (Lua scripts)
|
|
↓
|
|
Final DOM
|
|
```
|
|
|
|
### Lua Script Execution Flow
|
|
|
|
```
|
|
User Action
|
|
↓
|
|
Event Handler Triggered
|
|
↓
|
|
Lookup Script ID in Database
|
|
↓
|
|
Load Script Code
|
|
↓
|
|
Prepare Context (user, page, data)
|
|
↓
|
|
LuaEngine.execute(code, context)
|
|
↓
|
|
Sandboxed Lua VM
|
|
↓
|
|
Return Result
|
|
↓
|
|
Update UI / Navigate / Show Toast
|
|
```
|
|
|
|
## Adding New Content
|
|
|
|
### Add a New Page
|
|
|
|
```typescript
|
|
// In seed-data/pages.ts or via Level 4/5 GUI
|
|
|
|
const newPage: PageDefinition = {
|
|
id: 'page_my_feature',
|
|
level: 2,
|
|
title: 'My Feature',
|
|
layout: 'dashboard',
|
|
components: [
|
|
{
|
|
id: 'card_1',
|
|
type: 'Card',
|
|
props: { className: 'p-6' },
|
|
children: [
|
|
{
|
|
id: 'heading_1',
|
|
type: 'Heading',
|
|
props: { level: 2, children: 'My Feature' },
|
|
children: []
|
|
}
|
|
]
|
|
}
|
|
],
|
|
permissions: { requiresAuth: true }
|
|
}
|
|
|
|
await Database.addPage(newPage)
|
|
```
|
|
|
|
### Add a New Lua Script
|
|
|
|
```typescript
|
|
// In seed-data/scripts.ts
|
|
|
|
const newScript: LuaScript = {
|
|
id: 'script_my_logic',
|
|
name: 'My Custom Logic',
|
|
description: 'Does something useful',
|
|
code: `
|
|
function processData(input)
|
|
-- Your Lua logic here
|
|
return result
|
|
end
|
|
return processData
|
|
`,
|
|
parameters: [{ name: 'input', type: 'string' }],
|
|
returnType: 'string'
|
|
}
|
|
|
|
await Database.addLuaScript(newScript)
|
|
```
|
|
|
|
### Add a New Workflow
|
|
|
|
```typescript
|
|
// In seed-data/workflows.ts
|
|
|
|
const newWorkflow: Workflow = {
|
|
id: 'workflow_my_process',
|
|
name: 'My Process',
|
|
description: 'Multi-step process',
|
|
enabled: true,
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'trigger',
|
|
label: 'Start',
|
|
config: {},
|
|
position: { x: 100, y: 100 }
|
|
},
|
|
{
|
|
id: 'process',
|
|
type: 'lua',
|
|
label: 'Process Data',
|
|
config: {
|
|
scriptId: 'script_my_logic',
|
|
retry: { maxAttempts: 3, delayMs: 250, backoffMultiplier: 2 }
|
|
},
|
|
position: { x: 100, y: 200 }
|
|
}
|
|
],
|
|
edges: [
|
|
{ id: 'e1', source: 'start', target: 'process' }
|
|
]
|
|
}
|
|
|
|
await Database.addWorkflow(newWorkflow)
|
|
```
|
|
|
|
Note: Workflow nodes can include an optional `retry` config block to automatically retry failed nodes. Supported fields are `maxAttempts`, `delayMs`, `backoffMultiplier`, and `jitterMs`.
|
|
|
|
### Add a New Component Configuration
|
|
|
|
```typescript
|
|
// In seed-data/components.ts
|
|
|
|
const newConfig: ComponentConfig = {
|
|
id: 'config_my_button',
|
|
componentId: 'node_my_button',
|
|
props: {
|
|
children: 'Click Me',
|
|
variant: 'primary',
|
|
size: 'lg'
|
|
},
|
|
styles: {},
|
|
events: {
|
|
onClick: 'script_button_handler'
|
|
}
|
|
}
|
|
|
|
await Database.addComponentConfig(newConfig)
|
|
```
|
|
|
|
## Packages
|
|
|
|
Packages are self-contained collections of:
|
|
- Pages
|
|
- Components
|
|
- Scripts
|
|
- Workflows
|
|
- Assets
|
|
|
|
```
|
|
forum-package/
|
|
├── package.json
|
|
├── seed-data/
|
|
│ ├── pages.ts // Forum pages
|
|
│ ├── components.ts // Forum components
|
|
│ ├── scripts.ts // Forum logic
|
|
│ └── workflows.ts // Moderation workflows
|
|
├── assets/
|
|
│ └── images/
|
|
└── README.md
|
|
```
|
|
|
|
Install a package:
|
|
```typescript
|
|
await PackageManager.install('forum-package')
|
|
```
|
|
|
|
The package's seed data automatically merges with the main application.
|
|
|
|
## Extending the System
|
|
|
|
### Add a New Node Type for Workflows
|
|
|
|
```typescript
|
|
// In workflow-engine.ts
|
|
|
|
export class WorkflowEngine {
|
|
async executeNode(node: WorkflowNode, context: any) {
|
|
switch (node.type) {
|
|
case 'trigger':
|
|
return this.executeTrigger(node, context)
|
|
case 'action':
|
|
return this.executeAction(node, context)
|
|
case 'condition':
|
|
return this.executeCondition(node, context)
|
|
case 'lua':
|
|
return this.executeLuaScript(node, context)
|
|
|
|
// Add your new type here
|
|
case 'http_request':
|
|
return this.executeHttpRequest(node, context)
|
|
|
|
default:
|
|
throw new Error(`Unknown node type: ${node.type}`)
|
|
}
|
|
}
|
|
|
|
async executeHttpRequest(node: WorkflowNode, context: any) {
|
|
const { url, method, body } = node.config
|
|
const response = await fetch(url, { method, body })
|
|
return await response.json()
|
|
}
|
|
}
|
|
```
|
|
|
|
### Add a New Layout Type
|
|
|
|
```typescript
|
|
// In GenericPage.tsx
|
|
|
|
const renderLayout = () => {
|
|
switch (page.layout) {
|
|
case 'default':
|
|
return <DefaultLayout>{content}</DefaultLayout>
|
|
case 'sidebar':
|
|
return <SidebarLayout>{content}</SidebarLayout>
|
|
case 'dashboard':
|
|
return <DashboardLayout>{content}</DashboardLayout>
|
|
case 'blank':
|
|
return <>{content}</>
|
|
|
|
// Add your new layout
|
|
case 'split':
|
|
return <SplitLayout>{content}</SplitLayout>
|
|
|
|
default:
|
|
return <>{content}</>
|
|
}
|
|
}
|
|
```
|
|
|
|
### Add a New Permission Check
|
|
|
|
```typescript
|
|
// Create Lua script in seed-data/scripts.ts
|
|
|
|
{
|
|
id: 'script_custom_permission',
|
|
name: 'Custom Permission Check',
|
|
code: `
|
|
function checkCustomPermission(user, resource)
|
|
-- Your custom logic
|
|
if user.role == 'admin' then
|
|
return true
|
|
end
|
|
|
|
if resource.owner == user.id then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
return checkCustomPermission
|
|
`,
|
|
parameters: [
|
|
{ name: 'user', type: 'table' },
|
|
{ name: 'resource', type: 'table' }
|
|
],
|
|
returnType: 'boolean'
|
|
}
|
|
```
|
|
|
|
Then use in page definition:
|
|
```typescript
|
|
{
|
|
id: 'page_protected',
|
|
permissions: {
|
|
requiresAuth: true,
|
|
customCheck: 'script_custom_permission'
|
|
}
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Keep Infrastructure Minimal
|
|
Only create TSX files for:
|
|
- Generic renderers (GenericPage, RenderComponent)
|
|
- Core systems (Database, LuaEngine)
|
|
- UI components library (Shadcn)
|
|
|
|
### 2. Store Content as Data
|
|
Pages, workflows, scripts should be in database, not hardcoded.
|
|
|
|
### 3. Use Lua for Logic
|
|
Business logic belongs in Lua scripts, not TypeScript.
|
|
|
|
### 4. Modular Seed Data
|
|
Split large seed datasets across multiple files in `/src/seed-data/`.
|
|
|
|
### 5. Package Organization
|
|
Group related functionality into packages for distribution.
|
|
|
|
### 6. Type Safety
|
|
Use TypeScript types for infrastructure, but keep content flexible.
|
|
|
|
## Current Status
|
|
|
|
### Infrastructure (TypeScript - Must Keep)
|
|
- ✅ App.tsx
|
|
- ✅ GenericPage.tsx
|
|
- ✅ RenderComponent.tsx
|
|
- ✅ Database.ts
|
|
- ✅ LuaEngine.ts
|
|
- ✅ Shadcn components (~40 files)
|
|
|
|
### Content (Can Be Pure Data)
|
|
- ⚠️ Level1.tsx (to be replaced by GenericPage)
|
|
- ⚠️ Level2.tsx (to be replaced by GenericPage)
|
|
- ⚠️ Level3.tsx (to be replaced by GenericPage)
|
|
- ✅ All pages (via PageDefinitionBuilder)
|
|
- ✅ All scripts (Lua in database)
|
|
- ✅ All workflows (JSON in database)
|
|
- ✅ All components (configs in database)
|
|
|
|
## Roadmap
|
|
|
|
### Phase 3: Remove TSX Content Files
|
|
- [ ] Update App.tsx to use GenericPage for all levels
|
|
- [ ] Test Level 1/2/3 functionality in GenericPage
|
|
- [ ] Delete Level1.tsx, Level2.tsx, Level3.tsx
|
|
- [ ] Update documentation
|
|
|
|
### Phase 4: GUI Builders
|
|
- [ ] Visual page builder
|
|
- [ ] Workflow diagram editor
|
|
- [ ] Lua script IDE
|
|
- [ ] Component property editor
|
|
- [ ] Theme customizer
|
|
|
|
### Phase 5: Marketplace
|
|
- [ ] Package repository
|
|
- [ ] Community packages
|
|
- [ ] Version management
|
|
- [ ] Dependency resolution
|
|
|
|
## Conclusion
|
|
|
|
MetaBuilder's architecture is designed for maximum flexibility with minimal code dependencies. By storing everything as data and using generic renderers, the platform can grow infinitely without adding more TypeScript files.
|
|
|
|
**The goal: 95% data-driven, 5% infrastructure.**
|
|
|
|
---
|
|
|
|
*Last updated: Iteration 25*
|