ebade 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/CONTRIBUTING.md +177 -0
- package/LICENSE +21 -0
- package/MANIFESTO.md +170 -0
- package/README.md +263 -0
- package/ROADMAP.md +119 -0
- package/SYNTAX.md +515 -0
- package/benchmarks/RESULTS.md +119 -0
- package/benchmarks/token-benchmark.js +197 -0
- package/cli/scaffold.js +706 -0
- package/docs/GREEN-AI.md +86 -0
- package/examples/ecommerce.ebade.yaml +192 -0
- package/landing/favicon.svg +6 -0
- package/landing/index.html +227 -0
- package/landing/main.js +147 -0
- package/landing/og-image.png +0 -0
- package/landing/style.css +616 -0
- package/package.json +43 -0
- package/packages/mcp-server/README.md +144 -0
- package/packages/mcp-server/package-lock.json +1178 -0
- package/packages/mcp-server/package.json +32 -0
- package/packages/mcp-server/src/index.ts +316 -0
- package/packages/mcp-server/src/tools/compile.ts +269 -0
- package/packages/mcp-server/src/tools/generate.ts +420 -0
- package/packages/mcp-server/src/tools/scaffold.ts +474 -0
- package/packages/mcp-server/src/tools/validate.ts +233 -0
- package/packages/mcp-server/tsconfig.json +16 -0
- package/schema/project.schema.json +195 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ebade/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP Server for ebade - Enable AI agents to build with the essence of code",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ebade-mcp": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"ebade",
|
|
18
|
+
"ai",
|
|
19
|
+
"agent",
|
|
20
|
+
"framework"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
25
|
+
"yaml": "^2.3.4",
|
|
26
|
+
"zod": "^3.22.4"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.10.0",
|
|
30
|
+
"typescript": "^5.3.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ebade MCP Server
|
|
5
|
+
*
|
|
6
|
+
* This server enables AI agents to use ebade for building web applications.
|
|
7
|
+
* It exposes tools that allow agents to:
|
|
8
|
+
* - Scaffold projects from ebade definitions
|
|
9
|
+
* - Validate ebade files
|
|
10
|
+
* - Compile ebade to framework-specific code
|
|
11
|
+
* - Generate components from natural language descriptions
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
15
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
16
|
+
import {
|
|
17
|
+
CallToolRequestSchema,
|
|
18
|
+
ListToolsRequestSchema,
|
|
19
|
+
ListResourcesRequestSchema,
|
|
20
|
+
ReadResourceRequestSchema,
|
|
21
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
22
|
+
|
|
23
|
+
import { scaffoldProject } from "./tools/scaffold.js";
|
|
24
|
+
import { validateIntent } from "./tools/validate.js";
|
|
25
|
+
import { compileIntent } from "./tools/compile.js";
|
|
26
|
+
import { generateComponent } from "./tools/generate.js";
|
|
27
|
+
|
|
28
|
+
// Create the MCP server
|
|
29
|
+
const server = new Server(
|
|
30
|
+
{
|
|
31
|
+
name: "ebade",
|
|
32
|
+
version: "0.1.0",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
capabilities: {
|
|
36
|
+
tools: {},
|
|
37
|
+
resources: {},
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// List available tools
|
|
43
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
44
|
+
return {
|
|
45
|
+
tools: [
|
|
46
|
+
{
|
|
47
|
+
name: "ebade_scaffold",
|
|
48
|
+
description: `Scaffold a new project from an ebade definition.
|
|
49
|
+
This creates a complete Next.js project structure based on the ebade specification.
|
|
50
|
+
|
|
51
|
+
Use this when the user wants to:
|
|
52
|
+
- Start a new web project
|
|
53
|
+
- Create an e-commerce site, SaaS dashboard, blog, etc.
|
|
54
|
+
- Generate a full application structure
|
|
55
|
+
|
|
56
|
+
The ebade file uses YAML format with pages, components, data models, and API definitions.`,
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
projectName: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "Name of the project (kebab-case, e.g., 'my-store')",
|
|
63
|
+
},
|
|
64
|
+
projectType: {
|
|
65
|
+
type: "string",
|
|
66
|
+
enum: [
|
|
67
|
+
"e-commerce",
|
|
68
|
+
"saas-dashboard",
|
|
69
|
+
"blog",
|
|
70
|
+
"landing-page",
|
|
71
|
+
"portfolio",
|
|
72
|
+
"api-only",
|
|
73
|
+
],
|
|
74
|
+
description: "Type of project to scaffold",
|
|
75
|
+
},
|
|
76
|
+
features: {
|
|
77
|
+
type: "array",
|
|
78
|
+
items: { type: "string" },
|
|
79
|
+
description:
|
|
80
|
+
"Features to include (e.g., 'user-auth', 'shopping-cart', 'dark-mode')",
|
|
81
|
+
},
|
|
82
|
+
outputDir: {
|
|
83
|
+
type: "string",
|
|
84
|
+
description: "Output directory path (absolute)",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ["projectName", "projectType", "outputDir"],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "ebade_validate",
|
|
92
|
+
description: `Validate an ebade definition file.
|
|
93
|
+
Checks for:
|
|
94
|
+
- Correct syntax and structure
|
|
95
|
+
- Valid decorator usage
|
|
96
|
+
- Proper data model relationships
|
|
97
|
+
- Missing required fields
|
|
98
|
+
|
|
99
|
+
Use this before scaffolding to ensure the ebade is valid.`,
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: "object",
|
|
102
|
+
properties: {
|
|
103
|
+
intentContent: {
|
|
104
|
+
type: "string",
|
|
105
|
+
description: "The ebade file content (YAML or TypeScript)",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
required: ["intentContent"],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "ebade_compile",
|
|
113
|
+
description: `Compile a specific ebade definition into framework-specific code.
|
|
114
|
+
Returns the generated code for a specific page, component, or API route.
|
|
115
|
+
|
|
116
|
+
Use this when:
|
|
117
|
+
- User wants to see what code an ebade produces
|
|
118
|
+
- Building a specific component
|
|
119
|
+
- Iterating on a single ebade definition`,
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: "object",
|
|
122
|
+
properties: {
|
|
123
|
+
intent: {
|
|
124
|
+
type: "object",
|
|
125
|
+
description: "The ebade definition object",
|
|
126
|
+
properties: {
|
|
127
|
+
type: {
|
|
128
|
+
type: "string",
|
|
129
|
+
enum: ["page", "component", "api", "layout"],
|
|
130
|
+
},
|
|
131
|
+
name: { type: "string" },
|
|
132
|
+
path: { type: "string" },
|
|
133
|
+
components: {
|
|
134
|
+
type: "array",
|
|
135
|
+
items: { type: "string" },
|
|
136
|
+
},
|
|
137
|
+
auth: {
|
|
138
|
+
type: "string",
|
|
139
|
+
enum: ["none", "required", "optional"],
|
|
140
|
+
},
|
|
141
|
+
data: {
|
|
142
|
+
type: "array",
|
|
143
|
+
items: { type: "string" },
|
|
144
|
+
},
|
|
145
|
+
outcomes: {
|
|
146
|
+
type: "object",
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
target: {
|
|
151
|
+
type: "string",
|
|
152
|
+
enum: ["nextjs", "react", "vue", "svelte"],
|
|
153
|
+
description: "Target framework (default: nextjs)",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
required: ["intent"],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "ebade_generate",
|
|
161
|
+
description: `Generate a component from a natural language description.
|
|
162
|
+
AI-powered ebade inference - describe what you want and get the ebade + code.
|
|
163
|
+
|
|
164
|
+
Use this when:
|
|
165
|
+
- User describes a component in natural language
|
|
166
|
+
- Quick prototyping
|
|
167
|
+
- Exploring ebade syntax and capabilities`,
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
description: {
|
|
172
|
+
type: "string",
|
|
173
|
+
description:
|
|
174
|
+
"Natural language description of the component (e.g., 'a product card with image, title, price, and add to cart button')",
|
|
175
|
+
},
|
|
176
|
+
style: {
|
|
177
|
+
type: "string",
|
|
178
|
+
enum: [
|
|
179
|
+
"minimal-modern",
|
|
180
|
+
"bold-vibrant",
|
|
181
|
+
"dark-premium",
|
|
182
|
+
"glassmorphism",
|
|
183
|
+
],
|
|
184
|
+
description: "Design style preference",
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
required: ["description"],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Handle tool calls
|
|
195
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
196
|
+
const { name, arguments: args } = request.params;
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
switch (name) {
|
|
200
|
+
case "ebade_scaffold":
|
|
201
|
+
return {
|
|
202
|
+
content: [
|
|
203
|
+
{
|
|
204
|
+
type: "text",
|
|
205
|
+
text: await scaffoldProject(args as any),
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
case "ebade_validate":
|
|
211
|
+
return await validateIntent({
|
|
212
|
+
intentContent: args?.intentContent as string,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
case "ebade_compile":
|
|
216
|
+
return {
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "text",
|
|
220
|
+
text: await compileIntent({
|
|
221
|
+
intent: args?.intent as any,
|
|
222
|
+
target:
|
|
223
|
+
(args?.target as "nextjs" | "react" | "vue" | "svelte") ||
|
|
224
|
+
"nextjs",
|
|
225
|
+
}),
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
case "ebade_generate":
|
|
231
|
+
return await generateComponent({
|
|
232
|
+
description: args?.description as string,
|
|
233
|
+
style: args?.style as any,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
default:
|
|
237
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return {
|
|
241
|
+
content: [
|
|
242
|
+
{
|
|
243
|
+
type: "text",
|
|
244
|
+
text: `Error: ${
|
|
245
|
+
error instanceof Error ? error.message : String(error)
|
|
246
|
+
}`,
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
isError: true,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// List available resources (ebade templates, examples)
|
|
255
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
256
|
+
return {
|
|
257
|
+
resources: [
|
|
258
|
+
{
|
|
259
|
+
uri: "ebade://syntax",
|
|
260
|
+
name: "ebade Syntax Reference",
|
|
261
|
+
description: "The complete syntax guide for ebade definitions",
|
|
262
|
+
mimeType: "text/markdown",
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
uri: "ebade://examples/ecommerce",
|
|
266
|
+
name: "E-commerce ebade Example",
|
|
267
|
+
description: "A comprehensive ebade example for an e-commerce platform",
|
|
268
|
+
mimeType: "text/x-yaml",
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
uri: "ebade://examples/saas",
|
|
272
|
+
name: "SaaS Dashboard ebade Example",
|
|
273
|
+
description: "SaaS dashboard with auth, billing, user management",
|
|
274
|
+
mimeType: "text/x-yaml",
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
};
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Read resources
|
|
281
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
282
|
+
const { uri } = request.params;
|
|
283
|
+
|
|
284
|
+
const resources: Record<string, string> = {
|
|
285
|
+
"ebade://syntax": `# ebade Syntax Reference\\n\\n@page, @ebade, @requires, @outcomes, @data, @validate, @style, @compose, @on, @expects\\n\\nCode = f(ebade)`,
|
|
286
|
+
"ebade://examples/ecommerce": `# E-commerce ebade\nname: my-store\ntype: e-commerce\nfeatures:\n - product-catalog\n - shopping-cart\n - checkout`,
|
|
287
|
+
"ebade://examples/saas": `# SaaS ebade\nname: my-saas\ntype: saas-dashboard\nfeatures:\n - user-auth\n - billing\n - analytics`,
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const content = resources[uri];
|
|
291
|
+
if (!content) {
|
|
292
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
contents: [
|
|
297
|
+
{
|
|
298
|
+
uri,
|
|
299
|
+
mimeType: "text/markdown",
|
|
300
|
+
text: content,
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Start the server
|
|
307
|
+
async function main() {
|
|
308
|
+
const transport = new StdioServerTransport();
|
|
309
|
+
await server.connect(transport);
|
|
310
|
+
console.error("ebade MCP Server running on stdio");
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
main().catch((error) => {
|
|
314
|
+
console.error("Failed to start server:", error);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
});
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile Tool
|
|
3
|
+
*
|
|
4
|
+
* Compiles a single ebade definition into framework-specific code.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface CompileArgs {
|
|
8
|
+
intent: {
|
|
9
|
+
type: "page" | "component" | "api" | "layout";
|
|
10
|
+
name: string;
|
|
11
|
+
path?: string;
|
|
12
|
+
components?: string[];
|
|
13
|
+
auth?: "none" | "required" | "optional";
|
|
14
|
+
data?: string[];
|
|
15
|
+
outcomes?: Record<string, any>;
|
|
16
|
+
style?: string | Record<string, any>;
|
|
17
|
+
};
|
|
18
|
+
target?: "nextjs" | "react" | "vue" | "svelte";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Code generators for different targets
|
|
22
|
+
const generators = {
|
|
23
|
+
nextjs: {
|
|
24
|
+
page: (intent: CompileArgs["intent"]) => {
|
|
25
|
+
const name = intent.name
|
|
26
|
+
.split("-")
|
|
27
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
28
|
+
.join("");
|
|
29
|
+
|
|
30
|
+
const imports: string[] = [];
|
|
31
|
+
const bodyParts: string[] = [];
|
|
32
|
+
|
|
33
|
+
// Auth handling
|
|
34
|
+
if (intent.auth === "required") {
|
|
35
|
+
imports.push(`import { redirect } from "next/navigation";`);
|
|
36
|
+
imports.push(`import { auth } from "@/lib/auth";`);
|
|
37
|
+
bodyParts.push(` const session = await auth();
|
|
38
|
+
if (!session) {
|
|
39
|
+
redirect("/login");
|
|
40
|
+
}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Data fetching
|
|
44
|
+
if (intent.data && intent.data.length > 0) {
|
|
45
|
+
imports.push(`import { db } from "@/lib/db";`);
|
|
46
|
+
for (const dataName of intent.data) {
|
|
47
|
+
const varName = dataName.toLowerCase();
|
|
48
|
+
bodyParts.push(
|
|
49
|
+
` const ${varName} = await db.${varName}.findMany();`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Component imports
|
|
55
|
+
if (intent.components && intent.components.length > 0) {
|
|
56
|
+
for (const comp of intent.components) {
|
|
57
|
+
const compName = comp
|
|
58
|
+
.split("-")
|
|
59
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
60
|
+
.join("");
|
|
61
|
+
imports.push(`import { ${compName} } from "@/components/${comp}";`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Component usage
|
|
66
|
+
const componentJSX = (intent.components || [])
|
|
67
|
+
.map((comp) => {
|
|
68
|
+
const compName = comp
|
|
69
|
+
.split("-")
|
|
70
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
71
|
+
.join("");
|
|
72
|
+
return ` <${compName} />`;
|
|
73
|
+
})
|
|
74
|
+
.join("\n");
|
|
75
|
+
|
|
76
|
+
return `/**
|
|
77
|
+
* @page('${intent.path || "/"}')
|
|
78
|
+
* @ebade('${intent.name}')
|
|
79
|
+
* Built with ebade
|
|
80
|
+
* ${intent.auth ? `@requires({ auth: '${intent.auth}' })` : ""}
|
|
81
|
+
* ${
|
|
82
|
+
intent.data ? `@data([${intent.data.map((d) => `'${d}'`).join(", ")}])` : ""
|
|
83
|
+
}
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
${imports.join("\n")}
|
|
87
|
+
|
|
88
|
+
export default async function ${name}Page() {
|
|
89
|
+
${bodyParts.join("\n\n")}
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<main className="page ${intent.name}">
|
|
93
|
+
<div className="container">
|
|
94
|
+
${componentJSX || " {/* Page content */}"}
|
|
95
|
+
</div>
|
|
96
|
+
</main>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Metadata
|
|
101
|
+
export const metadata = {
|
|
102
|
+
title: "${name}",
|
|
103
|
+
description: "Page for ${intent.name} (Built with ebade)",
|
|
104
|
+
};
|
|
105
|
+
`;
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
component: (intent: CompileArgs["intent"]) => {
|
|
109
|
+
const name = intent.name
|
|
110
|
+
.split("-")
|
|
111
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
112
|
+
.join("");
|
|
113
|
+
|
|
114
|
+
const style =
|
|
115
|
+
typeof intent.style === "string"
|
|
116
|
+
? intent.style
|
|
117
|
+
: JSON.stringify(intent.style);
|
|
118
|
+
|
|
119
|
+
return `/* ebade Generated Component */
|
|
120
|
+
/**
|
|
121
|
+
* @ebade('${name}')
|
|
122
|
+
* ${intent.style ? `@style(${style})` : ""}
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
import { useState } from "react";
|
|
126
|
+
|
|
127
|
+
interface ${name}Props {
|
|
128
|
+
className?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function ${name}({ className }: ${name}Props) {
|
|
132
|
+
return (
|
|
133
|
+
<section className={\`${intent.name} \${className || ""}\`}>
|
|
134
|
+
<div className="container">
|
|
135
|
+
<h2>${name}</h2>
|
|
136
|
+
{/* Component content */}
|
|
137
|
+
</div>
|
|
138
|
+
</section>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
`;
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
api: (intent: CompileArgs["intent"]) => {
|
|
145
|
+
const authCheck =
|
|
146
|
+
intent.auth === "required"
|
|
147
|
+
? `
|
|
148
|
+
// Auth check
|
|
149
|
+
const session = await auth();
|
|
150
|
+
if (!session) {
|
|
151
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
152
|
+
}
|
|
153
|
+
`
|
|
154
|
+
: "";
|
|
155
|
+
|
|
156
|
+
return `/**
|
|
157
|
+
* @api('${intent.path || "/api/" + intent.name}')
|
|
158
|
+
* @ebade('${intent.name}')
|
|
159
|
+
* Built with ebade
|
|
160
|
+
* ${intent.auth ? `@requires({ auth: '${intent.auth}' })` : ""}
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
164
|
+
${intent.auth === "required" ? `import { auth } from "@/lib/auth";` : ""}
|
|
165
|
+
|
|
166
|
+
export async function GET(request: NextRequest) {
|
|
167
|
+
${authCheck}
|
|
168
|
+
try {
|
|
169
|
+
// TODO: Implement GET handler
|
|
170
|
+
return NextResponse.json({ message: "GET ${intent.name}" });
|
|
171
|
+
} catch (error) {
|
|
172
|
+
return NextResponse.json(
|
|
173
|
+
{ error: "Internal server error" },
|
|
174
|
+
{ status: 500 }
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function POST(request: NextRequest) {
|
|
180
|
+
${authCheck}
|
|
181
|
+
try {
|
|
182
|
+
const body = await request.json();
|
|
183
|
+
// TODO: Implement POST handler
|
|
184
|
+
return NextResponse.json({ message: "POST ${intent.name}", data: body });
|
|
185
|
+
} catch (error) {
|
|
186
|
+
return NextResponse.json(
|
|
187
|
+
{ error: "Internal server error" },
|
|
188
|
+
{ status: 500 }
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
`;
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
layout: (intent: CompileArgs["intent"]) => {
|
|
196
|
+
const name = intent.name
|
|
197
|
+
.split("-")
|
|
198
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
199
|
+
.join("");
|
|
200
|
+
|
|
201
|
+
return `/**
|
|
202
|
+
* ${name} Layout
|
|
203
|
+
*
|
|
204
|
+
* @layout('${intent.name}')
|
|
205
|
+
* ${
|
|
206
|
+
intent.components
|
|
207
|
+
? `@compose([${intent.components.map((c) => `'${c}'`).join(", ")}])`
|
|
208
|
+
: ""
|
|
209
|
+
}
|
|
210
|
+
*/
|
|
211
|
+
|
|
212
|
+
${(intent.components || [])
|
|
213
|
+
.map((comp) => {
|
|
214
|
+
const compName = comp
|
|
215
|
+
.split("-")
|
|
216
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
217
|
+
.join("");
|
|
218
|
+
return `import { ${compName} } from "@/components/${comp}";`;
|
|
219
|
+
})
|
|
220
|
+
.join("\n")}
|
|
221
|
+
|
|
222
|
+
export default function ${name}Layout({
|
|
223
|
+
children,
|
|
224
|
+
}: {
|
|
225
|
+
children: React.ReactNode;
|
|
226
|
+
}) {
|
|
227
|
+
return (
|
|
228
|
+
<div className="layout ${intent.name}">
|
|
229
|
+
${intent.components?.includes("header") ? "<Header />" : ""}
|
|
230
|
+
<main>{children}</main>
|
|
231
|
+
${intent.components?.includes("footer") ? "<Footer />" : ""}
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export async function compileIntent(args: CompileArgs) {
|
|
241
|
+
const { intent, target = "nextjs" } = args;
|
|
242
|
+
|
|
243
|
+
if (target !== "nextjs") {
|
|
244
|
+
return `⚠️ Target "${target}" is not yet supported. Currently only "nextjs" is available.
|
|
245
|
+
|
|
246
|
+
Coming soon: vue, svelte, react (standalone)`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const generator = generators[target];
|
|
250
|
+
const typeGenerator = (generator as any)[intent.type];
|
|
251
|
+
|
|
252
|
+
if (!typeGenerator) {
|
|
253
|
+
return `❌ Unknown ebade type: ${intent.type}. Available: page, component, api, layout`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const code = typeGenerator(intent);
|
|
257
|
+
|
|
258
|
+
return `✅ Compiled ${intent.type}: ${intent.name}
|
|
259
|
+
|
|
260
|
+
\`\`\`typescript
|
|
261
|
+
${code}
|
|
262
|
+
\`\`\`
|
|
263
|
+
|
|
264
|
+
📋 ebade definition used:
|
|
265
|
+
\`\`\`json
|
|
266
|
+
${JSON.stringify(intent, null, 2)}
|
|
267
|
+
\`\`\`
|
|
268
|
+
`;
|
|
269
|
+
}
|