mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
docs: json,script,pattern (2 files)
This commit is contained in:
736
SCRIPT_JSON_PATTERN.md
Normal file
736
SCRIPT_JSON_PATTERN.md
Normal file
@@ -0,0 +1,736 @@
|
||||
# Script.json - Complete JavaScript/TypeScript Abstraction
|
||||
|
||||
## Concept
|
||||
|
||||
Just as `styles.json` abstracts CSS into declarative JSON, **`script.json` abstracts executable JavaScript/TypeScript** - functions, promises, async/await, classes, React hooks, generators, and all runtime behavior.
|
||||
|
||||
## Why This Matters
|
||||
|
||||
**Everything as Data**: When code is represented as JSON:
|
||||
- **Non-developers** can understand and modify logic
|
||||
- **AI/LLMs** can reason about and generate code more reliably
|
||||
- **Visual editors** can provide drag-and-drop programming
|
||||
- **Code generation** becomes schema transformation
|
||||
- **Multi-language output** from a single source (TS, JS, Rust, Python)
|
||||
- **Version control** shows semantic changes, not syntax changes
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
script.json (Abstract Syntax)
|
||||
↓
|
||||
ScriptCompiler
|
||||
↓
|
||||
TypeScript/JavaScript Output
|
||||
↓
|
||||
Runtime Execution
|
||||
```
|
||||
|
||||
## Schema Structure
|
||||
|
||||
### 1. Module System
|
||||
|
||||
```json
|
||||
{
|
||||
"imports": [
|
||||
{
|
||||
"module": "react",
|
||||
"imports": ["useState", "useEffect"],
|
||||
"type": "named"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Compiles to:
|
||||
```typescript
|
||||
import { useState, useEffect } from 'react';
|
||||
```
|
||||
|
||||
### 2. Constants & Variables
|
||||
|
||||
```json
|
||||
{
|
||||
"constants": [
|
||||
{
|
||||
"name": "API_BASE_URL",
|
||||
"value": "https://api.example.com",
|
||||
"type": "string",
|
||||
"exported": true
|
||||
}
|
||||
],
|
||||
"variables": [
|
||||
{
|
||||
"name": "cacheStore",
|
||||
"kind": "let",
|
||||
"initialValue": {
|
||||
"expression": "new Map()",
|
||||
"type": "constructor"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Compiles to:
|
||||
```typescript
|
||||
export const API_BASE_URL = "https://api.example.com";
|
||||
let cacheStore = new Map();
|
||||
```
|
||||
|
||||
### 3. Functions (Async/Sync)
|
||||
|
||||
```json
|
||||
{
|
||||
"functions": [
|
||||
{
|
||||
"name": "fetchUserData",
|
||||
"async": true,
|
||||
"params": [
|
||||
{ "name": "userId", "type": "string" }
|
||||
],
|
||||
"returnType": "Promise<User>",
|
||||
"body": [
|
||||
{
|
||||
"type": "const_declaration",
|
||||
"name": "response",
|
||||
"value": {
|
||||
"type": "await",
|
||||
"expression": {
|
||||
"type": "call_expression",
|
||||
"callee": "fetch",
|
||||
"args": ["/api/users/${userId}"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"value": "$ref:local.response"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Compiles to:
|
||||
```typescript
|
||||
async function fetchUserData(userId: string): Promise<User> {
|
||||
const response = await fetch(`/api/users/${userId}`);
|
||||
return response;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Control Flow
|
||||
|
||||
**If Statements**:
|
||||
```json
|
||||
{
|
||||
"type": "if_statement",
|
||||
"condition": {
|
||||
"type": "binary_expression",
|
||||
"left": "$ref:local.count",
|
||||
"operator": ">",
|
||||
"right": 0
|
||||
},
|
||||
"then": [
|
||||
{ "type": "return", "value": true }
|
||||
],
|
||||
"else": [
|
||||
{ "type": "return", "value": false }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Loops**:
|
||||
```json
|
||||
{
|
||||
"type": "for_loop",
|
||||
"init": { "type": "const_declaration", "name": "i", "value": 0 },
|
||||
"condition": {
|
||||
"type": "binary_expression",
|
||||
"left": "$ref:local.i",
|
||||
"operator": "<",
|
||||
"right": 10
|
||||
},
|
||||
"update": {
|
||||
"type": "update_expression",
|
||||
"operator": "++",
|
||||
"argument": "$ref:local.i"
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "call_expression",
|
||||
"callee": "console.log",
|
||||
"args": ["$ref:local.i"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Error Handling
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "try_catch",
|
||||
"try": [
|
||||
{
|
||||
"type": "const_declaration",
|
||||
"name": "data",
|
||||
"value": {
|
||||
"type": "await",
|
||||
"expression": {
|
||||
"type": "call_expression",
|
||||
"callee": "fetchData",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"catch": {
|
||||
"param": "error",
|
||||
"body": [
|
||||
{
|
||||
"type": "call_expression",
|
||||
"callee": "console.error",
|
||||
"args": ["$ref:catch.error"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"finally": [
|
||||
{
|
||||
"type": "call_expression",
|
||||
"callee": "cleanup",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Classes
|
||||
|
||||
```json
|
||||
{
|
||||
"classes": [
|
||||
{
|
||||
"name": "ApiClient",
|
||||
"properties": [
|
||||
{ "name": "baseUrl", "type": "string", "visibility": "private" }
|
||||
],
|
||||
"constructor": {
|
||||
"params": [{ "name": "config", "type": "object" }],
|
||||
"body": [
|
||||
{
|
||||
"type": "assignment",
|
||||
"target": "this.baseUrl",
|
||||
"value": "$ref:params.config.baseUrl"
|
||||
}
|
||||
]
|
||||
},
|
||||
"methods": [
|
||||
{
|
||||
"name": "get",
|
||||
"async": true,
|
||||
"visibility": "public",
|
||||
"params": [{ "name": "endpoint", "type": "string" }],
|
||||
"returnType": "Promise<any>",
|
||||
"body": [...]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 7. React Hooks
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"name": "useFetch",
|
||||
"params": [{ "name": "url", "type": "string" }],
|
||||
"body": [
|
||||
{
|
||||
"type": "const_declaration",
|
||||
"name": "[data, setData]",
|
||||
"value": {
|
||||
"type": "call_expression",
|
||||
"callee": "useState",
|
||||
"args": [null]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "call_expression",
|
||||
"callee": "useEffect",
|
||||
"args": [
|
||||
{
|
||||
"type": "arrow_function",
|
||||
"async": true,
|
||||
"body": [...]
|
||||
},
|
||||
["$ref:params.url"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"value": {
|
||||
"type": "object_literal",
|
||||
"properties": {
|
||||
"data": "$ref:local.data"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Promises & Async Operations
|
||||
|
||||
```json
|
||||
{
|
||||
"functions": [
|
||||
{
|
||||
"name": "retryWithBackoff",
|
||||
"async": true,
|
||||
"params": [
|
||||
{ "name": "fn", "type": "() => Promise<T>" },
|
||||
{ "name": "maxRetries", "type": "number" }
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"type": "for_loop",
|
||||
"init": { "name": "i", "value": 0 },
|
||||
"condition": { "left": "$ref:local.i", "operator": "<", "right": "$ref:params.maxRetries" },
|
||||
"body": [
|
||||
{
|
||||
"type": "try_catch",
|
||||
"try": [
|
||||
{
|
||||
"type": "return",
|
||||
"value": {
|
||||
"type": "await",
|
||||
"expression": {
|
||||
"type": "call_expression",
|
||||
"callee": "$ref:params.fn"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"catch": {
|
||||
"body": [
|
||||
{
|
||||
"type": "await",
|
||||
"expression": {
|
||||
"type": "call_expression",
|
||||
"callee": "delay",
|
||||
"args": [1000]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 9. Generators
|
||||
|
||||
```json
|
||||
{
|
||||
"generators": [
|
||||
{
|
||||
"name": "range",
|
||||
"generator": true,
|
||||
"params": [
|
||||
{ "name": "start", "type": "number" },
|
||||
{ "name": "end", "type": "number" }
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"type": "for_loop",
|
||||
"init": { "name": "i", "value": "$ref:params.start" },
|
||||
"condition": { "left": "$ref:local.i", "operator": "<=", "right": "$ref:params.end" },
|
||||
"body": [
|
||||
{ "type": "yield", "value": "$ref:local.i" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 10. Event Handlers
|
||||
|
||||
```json
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"name": "UserLoggedInEvent",
|
||||
"type": "CustomEvent",
|
||||
"detail": {
|
||||
"userId": "string",
|
||||
"timestamp": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"event_handlers": [
|
||||
{
|
||||
"event": "$ref:events.user_logged_in_event",
|
||||
"handler": {
|
||||
"type": "arrow_function",
|
||||
"params": [{ "name": "event", "type": "UserLoggedInEvent" }],
|
||||
"body": [
|
||||
{
|
||||
"type": "call_expression",
|
||||
"callee": "console.log",
|
||||
"args": ["User logged in:", "$ref:params.event.detail.userId"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Reference System
|
||||
|
||||
The `$ref:` syntax creates references to:
|
||||
- **`$ref:params.userId`** - Function parameters
|
||||
- **`$ref:local.response`** - Local variables
|
||||
- **`$ref:constants.API_BASE_URL`** - Module constants
|
||||
- **`$ref:imports.axios.get`** - Imported functions
|
||||
- **`$ref:functions.delay`** - Other functions in the module
|
||||
- **`$ref:catch.error`** - Catch block error parameter
|
||||
|
||||
This enables:
|
||||
- Type safety
|
||||
- Dependency tracking
|
||||
- Refactoring without string replacements
|
||||
- Dead code elimination
|
||||
|
||||
## Compiler Implementation
|
||||
|
||||
```typescript
|
||||
class ScriptCompiler {
|
||||
private schema: ScriptSchema;
|
||||
|
||||
compile(): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
// Imports
|
||||
if (this.schema.imports) {
|
||||
parts.push(this.compileImports(this.schema.imports));
|
||||
}
|
||||
|
||||
// Constants
|
||||
if (this.schema.constants) {
|
||||
parts.push(this.compileConstants(this.schema.constants));
|
||||
}
|
||||
|
||||
// Functions
|
||||
if (this.schema.functions) {
|
||||
parts.push(...this.schema.functions.map(f => this.compileFunction(f)));
|
||||
}
|
||||
|
||||
// Classes
|
||||
if (this.schema.classes) {
|
||||
parts.push(...this.schema.classes.map(c => this.compileClass(c)));
|
||||
}
|
||||
|
||||
// Hooks
|
||||
if (this.schema.hooks) {
|
||||
parts.push(...this.schema.hooks.map(h => this.compileHook(h)));
|
||||
}
|
||||
|
||||
return parts.join('\n\n');
|
||||
}
|
||||
|
||||
private compileFunction(def: FunctionDefinition): string {
|
||||
const asyncKw = def.async ? 'async ' : '';
|
||||
const exportKw = def.exported ? 'export ' : '';
|
||||
const params = def.params.map(p =>
|
||||
`${p.name}${p.optional ? '?' : ''}: ${p.type}`
|
||||
).join(', ');
|
||||
|
||||
const body = this.compileBody(def.body);
|
||||
|
||||
return `${exportKw}${asyncKw}function ${def.name}(${params}): ${def.returnType} {\n${body}\n}`;
|
||||
}
|
||||
|
||||
private compileBody(statements: Statement[]): string {
|
||||
return statements.map(stmt => this.compileStatement(stmt)).join('\n');
|
||||
}
|
||||
|
||||
private compileStatement(stmt: Statement): string {
|
||||
switch (stmt.type) {
|
||||
case 'const_declaration':
|
||||
return ` const ${stmt.name} = ${this.compileExpression(stmt.value)};`;
|
||||
|
||||
case 'if_statement':
|
||||
const condition = this.compileExpression(stmt.condition);
|
||||
const thenBlock = this.compileBody(stmt.then);
|
||||
const elseBlock = stmt.else ? `\n } else {\n${this.compileBody(stmt.else)}` : '';
|
||||
return ` if (${condition}) {\n${thenBlock}${elseBlock}\n }`;
|
||||
|
||||
case 'return':
|
||||
return ` return ${this.compileExpression(stmt.value)};`;
|
||||
|
||||
case 'await':
|
||||
return `await ${this.compileExpression(stmt.expression)}`;
|
||||
|
||||
// ... more statement types
|
||||
}
|
||||
}
|
||||
|
||||
private compileExpression(expr: Expression): string {
|
||||
if (typeof expr === 'string' && expr.startsWith('$ref:')) {
|
||||
return this.resolveReference(expr);
|
||||
}
|
||||
|
||||
if (expr.type === 'call_expression') {
|
||||
const args = expr.args.map(a => this.compileExpression(a)).join(', ');
|
||||
return `${expr.callee}(${args})`;
|
||||
}
|
||||
|
||||
if (expr.type === 'template_literal') {
|
||||
return `\`${expr.template}\``;
|
||||
}
|
||||
|
||||
// ... more expression types
|
||||
}
|
||||
|
||||
private resolveReference(ref: string): string {
|
||||
// $ref:params.userId -> userId
|
||||
// $ref:local.response -> response
|
||||
// $ref:constants.API_BASE_URL -> API_BASE_URL
|
||||
const parts = ref.replace('$ref:', '').split('.');
|
||||
return parts[parts.length - 1];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
**Input**: `packages/shared/seed/script.json`
|
||||
|
||||
**Compile**:
|
||||
```typescript
|
||||
const compiler = new ScriptCompiler(scriptSchema);
|
||||
const tsCode = compiler.compile();
|
||||
fs.writeFileSync('packages/shared/dist/index.ts', tsCode);
|
||||
```
|
||||
|
||||
**Output**: `packages/shared/dist/index.ts`
|
||||
```typescript
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
export const API_BASE_URL = "https://api.example.com";
|
||||
export const MAX_RETRIES = 3;
|
||||
|
||||
let cacheStore = new Map();
|
||||
|
||||
export async function fetchUserData(userId: string, options?: object): Promise<User> {
|
||||
const cacheKey = `user_${userId}`;
|
||||
|
||||
if (cacheStore.has(cacheKey)) {
|
||||
return cacheStore.get(cacheKey);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE_URL}/users/${userId}`, options);
|
||||
const userData = response.data;
|
||||
cacheStore.set(cacheKey, userData);
|
||||
return userData;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch user:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function retryWithBackoff<T>(
|
||||
fn: () => Promise<T>,
|
||||
maxRetries: number = MAX_RETRIES
|
||||
): Promise<T> {
|
||||
for (const i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error) {
|
||||
if (i === maxRetries - 1) {
|
||||
throw error;
|
||||
}
|
||||
await delay(1000 * Math.pow(2, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function delay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export class ApiClient {
|
||||
private baseUrl: string;
|
||||
private timeout: number;
|
||||
|
||||
constructor(config: { baseUrl: string; timeout?: number }) {
|
||||
this.baseUrl = config.baseUrl;
|
||||
this.timeout = config.timeout ?? 5000;
|
||||
}
|
||||
|
||||
async get<T>(endpoint: string): Promise<T> {
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
signal: AbortSignal.timeout(this.timeout)
|
||||
});
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async post<T, R>(endpoint: string, data: T): Promise<R> {
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
export function useFetch<T>(url: string) {
|
||||
const [data, setData] = useState<T | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(async () => {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const json = await response.json();
|
||||
setData(json);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
return { data, loading, error };
|
||||
}
|
||||
|
||||
export function* range(start: number, end: number): Generator<number> {
|
||||
for (let i = start; i <= end; i++) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
### 1. **Visual Programming**
|
||||
Build a drag-and-drop editor where users create functions by connecting blocks.
|
||||
|
||||
### 2. **Multi-Language Output**
|
||||
Same `script.json` → TypeScript, JavaScript, Python, Rust
|
||||
|
||||
### 3. **AI-Friendly**
|
||||
LLMs can generate/modify JSON more reliably than raw code.
|
||||
|
||||
### 4. **Static Analysis**
|
||||
Analyze control flow, detect dead code, optimize without parsing.
|
||||
|
||||
### 5. **Runtime Flexibility**
|
||||
Interpret JSON directly without compilation (like a scripting engine).
|
||||
|
||||
### 6. **Educational**
|
||||
Teach programming concepts with visual JSON representation.
|
||||
|
||||
## Integration with Existing Patterns
|
||||
|
||||
| Pattern | File | Purpose |
|
||||
|---------|------|---------|
|
||||
| **UI Components** | `components.json` | Component tree structure |
|
||||
| **Styling** | `styles.json` | CSS abstraction |
|
||||
| **Types** | `types.json` | TypeScript type definitions |
|
||||
| **Logic** | `script.json` | **Executable code** |
|
||||
|
||||
Together, these define a **complete application** in pure JSON:
|
||||
```
|
||||
packages/my_app/seed/
|
||||
├── components.json (What to render)
|
||||
├── styles.json (How it looks)
|
||||
├── types.json (Type system)
|
||||
└── script.json (How it behaves)
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Composition
|
||||
```json
|
||||
{
|
||||
"composition": [
|
||||
{
|
||||
"name": "enhancedFetch",
|
||||
"base": "$ref:functions.fetchUserData",
|
||||
"middleware": [
|
||||
"$ref:functions.retryWithBackoff",
|
||||
"$ref:functions.cacheWrapper"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Optimization Hints
|
||||
```json
|
||||
{
|
||||
"functions": [
|
||||
{
|
||||
"name": "expensiveCalculation",
|
||||
"memoize": true,
|
||||
"pure": true,
|
||||
"body": [...]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Dependency Injection
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"logger": "$ref:services.logger",
|
||||
"cache": "$ref:services.cache"
|
||||
},
|
||||
"functions": [
|
||||
{
|
||||
"name": "processData",
|
||||
"inject": ["logger", "cache"],
|
||||
"body": [...]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
**script.json** demonstrates that **executable code is just data**. When properly abstracted:
|
||||
- Logic becomes composable
|
||||
- Non-developers can participate
|
||||
- AI can reason about behavior
|
||||
- Visual tools can edit functionality
|
||||
- Cross-language compilation is trivial
|
||||
|
||||
This completes the abstraction trilogy:
|
||||
1. **styles.json** = Appearance as data
|
||||
2. **types.json** = Type system as data
|
||||
3. **script.json** = Behavior as data
|
||||
|
||||
**Everything is JSON. Everything is composable. Everything is programmable.**
|
||||
@@ -42,10 +42,16 @@
|
||||
"export/to_scss.lua",
|
||||
"export/to_css.lua"
|
||||
],
|
||||
"tests": [
|
||||
"tests/colors.test.lua",
|
||||
"tests/export.test.lua"
|
||||
],
|
||||
"tests": {
|
||||
"scripts": [
|
||||
"tests/colors.test.lua",
|
||||
"tests/export.test.lua"
|
||||
],
|
||||
"cases": [
|
||||
"tests/colors.cases.json",
|
||||
"tests/export.cases.json"
|
||||
]
|
||||
},
|
||||
"primary": true,
|
||||
"permissions": {
|
||||
"css.designer.export": {
|
||||
|
||||
Reference in New Issue
Block a user