@flareflow/cli 0.0.1
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/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +228 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/studio.html +288 -0
- package/package.json +33 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cac } from "cac";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { build } from "esbuild";
|
|
6
|
+
const cli = cac("flareflow");
|
|
7
|
+
async function loadConfig() {
|
|
8
|
+
const configPath = path.resolve(process.cwd(), "flareflow.config.ts");
|
|
9
|
+
if (!fs.existsSync(configPath))
|
|
10
|
+
return null;
|
|
11
|
+
const outDir = path.join(process.cwd(), ".flareflow");
|
|
12
|
+
if (!fs.existsSync(outDir))
|
|
13
|
+
fs.mkdirSync(outDir);
|
|
14
|
+
const outFile = path.join(outDir, "config.js");
|
|
15
|
+
await build({
|
|
16
|
+
entryPoints: [configPath],
|
|
17
|
+
outfile: outFile,
|
|
18
|
+
bundle: true,
|
|
19
|
+
format: "cjs",
|
|
20
|
+
platform: "node",
|
|
21
|
+
});
|
|
22
|
+
const config = require(outFile);
|
|
23
|
+
return config.default || config;
|
|
24
|
+
}
|
|
25
|
+
function generateWranglerToml(config) {
|
|
26
|
+
let toml = `name = "${config.name}"\n`;
|
|
27
|
+
toml += `main = "src/index.ts"\n`;
|
|
28
|
+
toml += `compatibility_date = "${config.compatibilityDate || "2024-04-05"}"\n\n`;
|
|
29
|
+
if (config.resources?.db) {
|
|
30
|
+
toml += `[[d1_databases]]\nbinding = "DB"\ndatabase_name = "${config.resources.db.name}"\ndatabase_id = "${config.resources.db.database_id}"\n\n`;
|
|
31
|
+
}
|
|
32
|
+
if (config.resources?.cache) {
|
|
33
|
+
toml += `[[kv_namespaces]]\nbinding = "CACHE"\nid = "${config.resources.cache.id}"\n\n`;
|
|
34
|
+
}
|
|
35
|
+
// Durable Objects
|
|
36
|
+
const doClasses = [
|
|
37
|
+
...(config.actors || []),
|
|
38
|
+
...(config.rooms || []),
|
|
39
|
+
...(config.workflows || []),
|
|
40
|
+
];
|
|
41
|
+
if (doClasses.length > 0) {
|
|
42
|
+
toml += `[durable_objects]\nbindings = [\n`;
|
|
43
|
+
for (const cls of doClasses) {
|
|
44
|
+
toml += ` { name = "${cls}", class_name = "${cls}" },\n`;
|
|
45
|
+
}
|
|
46
|
+
toml += `]\n\n`;
|
|
47
|
+
toml += `[[migrations]]\ntag = "v${Date.now()}"\nnew_classes = [${doClasses.map(c => `"${c}"`).join(", ")}]\n\n`;
|
|
48
|
+
}
|
|
49
|
+
fs.writeFileSync(path.join(process.cwd(), "wrangler.toml"), toml);
|
|
50
|
+
}
|
|
51
|
+
cli
|
|
52
|
+
.command("init <dir>", "Initialize a new flareflow project")
|
|
53
|
+
.action((dir) => {
|
|
54
|
+
const targetDir = path.resolve(process.cwd(), dir);
|
|
55
|
+
if (!fs.existsSync(targetDir))
|
|
56
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
57
|
+
const packageJson = {
|
|
58
|
+
name: path.basename(dir),
|
|
59
|
+
version: "0.0.1",
|
|
60
|
+
private: true,
|
|
61
|
+
scripts: {
|
|
62
|
+
dev: "flareflow dev",
|
|
63
|
+
deploy: "flareflow deploy",
|
|
64
|
+
"db:generate": "flareflow db:generate",
|
|
65
|
+
"db:migrate": "flareflow db:migrate"
|
|
66
|
+
},
|
|
67
|
+
dependencies: {
|
|
68
|
+
"@flareflow/core": "0.0.1",
|
|
69
|
+
"@flareflow/db": "0.0.1",
|
|
70
|
+
"@flareflow/actor": "0.0.1",
|
|
71
|
+
"@flareflow/realtime": "0.0.1",
|
|
72
|
+
"@flareflow/workflow": "0.0.1",
|
|
73
|
+
"@flareflow/auth": "0.0.1"
|
|
74
|
+
},
|
|
75
|
+
devDependencies: {
|
|
76
|
+
"typescript": "^5.4.5",
|
|
77
|
+
"wrangler": "^3.50.0",
|
|
78
|
+
"@flareflow/cli": "0.0.1"
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
fs.writeFileSync(path.join(targetDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
82
|
+
const configTs = `import { defineConfig } from "@flareflow/cli";
|
|
83
|
+
|
|
84
|
+
export default defineConfig({
|
|
85
|
+
name: "${path.basename(dir)}",
|
|
86
|
+
compatibilityDate: "2024-04-05"
|
|
87
|
+
});
|
|
88
|
+
`;
|
|
89
|
+
fs.writeFileSync(path.join(targetDir, "flareflow.config.ts"), configTs);
|
|
90
|
+
const srcDir = path.join(targetDir, "src");
|
|
91
|
+
if (!fs.existsSync(srcDir))
|
|
92
|
+
fs.mkdirSync(srcDir);
|
|
93
|
+
const indexTs = `import { createApp } from "@flareflow/core";
|
|
94
|
+
|
|
95
|
+
const app = createApp({
|
|
96
|
+
name: "${path.basename(dir)}"
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
app.get("/", async (ctx) => {
|
|
100
|
+
return { message: "Hello from flareflow!" };
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
export default app.export();
|
|
104
|
+
`;
|
|
105
|
+
fs.writeFileSync(path.join(srcDir, "index.ts"), indexTs);
|
|
106
|
+
console.log(`flareflow project initialized in ${targetDir}`);
|
|
107
|
+
console.log(`Run: cd ${dir} && npm install`);
|
|
108
|
+
});
|
|
109
|
+
cli
|
|
110
|
+
.command("generate <type> <name>", "Generate boilerplate for entities, actors, etc.")
|
|
111
|
+
.action((type, name) => {
|
|
112
|
+
const srcDir = path.join(process.cwd(), "src");
|
|
113
|
+
if (!fs.existsSync(srcDir))
|
|
114
|
+
fs.mkdirSync(srcDir);
|
|
115
|
+
const plural = type === "entity" ? "entities" : `${type}s`;
|
|
116
|
+
const typeDir = path.join(srcDir, plural);
|
|
117
|
+
if (!fs.existsSync(typeDir))
|
|
118
|
+
fs.mkdirSync(typeDir);
|
|
119
|
+
let template = "";
|
|
120
|
+
const fileName = `${name.toLowerCase()}.${type}.ts`;
|
|
121
|
+
switch (type) {
|
|
122
|
+
case "entity":
|
|
123
|
+
template = `import { entity, field } from "@flareflow/db";
|
|
124
|
+
|
|
125
|
+
export const ${name} = entity("${name.toLowerCase()}s", {
|
|
126
|
+
id: field.id(),
|
|
127
|
+
createdAt: field.timestamp("createdAt"),
|
|
128
|
+
});
|
|
129
|
+
`;
|
|
130
|
+
break;
|
|
131
|
+
case "actor":
|
|
132
|
+
template = `import { actor } from "@flareflow/actor";
|
|
133
|
+
|
|
134
|
+
export const ${name}Actor = actor("${name}Actor", {
|
|
135
|
+
state: {},
|
|
136
|
+
methods: {
|
|
137
|
+
async ping(ctx) {
|
|
138
|
+
return "pong";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
`;
|
|
143
|
+
break;
|
|
144
|
+
case "job":
|
|
145
|
+
template = `import { job } from "@flareflow/jobs";
|
|
146
|
+
|
|
147
|
+
export const ${name}Job = job("${name.toLowerCase()}", {
|
|
148
|
+
handler: async (ctx, payload) => {
|
|
149
|
+
console.log("Running ${name}Job", payload);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
`;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
if (template) {
|
|
156
|
+
fs.writeFileSync(path.join(typeDir, fileName), template);
|
|
157
|
+
console.log(`Generated ${type} ${name} in src/${plural}/${fileName}`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
console.error(`Unknown type: ${type}`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
cli
|
|
164
|
+
.command("dev", "Start local development server")
|
|
165
|
+
.action(async () => {
|
|
166
|
+
const config = await loadConfig();
|
|
167
|
+
if (config)
|
|
168
|
+
generateWranglerToml(config);
|
|
169
|
+
console.log("Starting wrangler dev...");
|
|
170
|
+
const { spawnSync } = require("child_process");
|
|
171
|
+
spawnSync("npx", ["wrangler", "dev"], { stdio: "inherit" });
|
|
172
|
+
});
|
|
173
|
+
cli
|
|
174
|
+
.command("deploy", "Deploy to Flareflow Workers")
|
|
175
|
+
.action(async () => {
|
|
176
|
+
const config = await loadConfig();
|
|
177
|
+
if (config)
|
|
178
|
+
generateWranglerToml(config);
|
|
179
|
+
console.log("Deploying via wrangler...");
|
|
180
|
+
const { spawnSync } = require("child_process");
|
|
181
|
+
spawnSync("npx", ["wrangler", "deploy"], { stdio: "inherit" });
|
|
182
|
+
});
|
|
183
|
+
cli
|
|
184
|
+
.command("db:generate", "Generate migrations from entities")
|
|
185
|
+
.action(() => {
|
|
186
|
+
console.log("Generating migrations via drizzle-kit...");
|
|
187
|
+
// TODO: Invoke drizzle-kit
|
|
188
|
+
});
|
|
189
|
+
cli
|
|
190
|
+
.command("db:migrate", "Apply migrations to D1")
|
|
191
|
+
.action(() => {
|
|
192
|
+
console.log("Applying migrations via drizzle-kit...");
|
|
193
|
+
// TODO: Invoke drizzle-kit
|
|
194
|
+
});
|
|
195
|
+
cli
|
|
196
|
+
.command("studio", "Start flareflow Studio")
|
|
197
|
+
.action(() => {
|
|
198
|
+
const http = require("http");
|
|
199
|
+
const fs = require("fs");
|
|
200
|
+
const path = require("path");
|
|
201
|
+
const server = http.createServer((req, res) => {
|
|
202
|
+
const studioPath = path.join(__dirname, "studio.html");
|
|
203
|
+
if (fs.existsSync(studioPath)) {
|
|
204
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
205
|
+
res.end(fs.readFileSync(studioPath));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// Fallback for development if not in dist
|
|
209
|
+
const devPath = path.join(process.cwd(), "packages/cli/src/studio.html");
|
|
210
|
+
if (fs.existsSync(devPath)) {
|
|
211
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
212
|
+
res.end(fs.readFileSync(devPath));
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
res.writeHead(404);
|
|
216
|
+
res.end("Studio dashboard not found. Please run build.");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
server.listen(4000, () => {
|
|
221
|
+
console.log("🚀 flareflow Studio started at http://localhost:4000");
|
|
222
|
+
console.log("Connects to local app at http://localhost:8787");
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
cli.help();
|
|
226
|
+
cli.version("0.0.1");
|
|
227
|
+
cli.parse();
|
|
228
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface FlareflowConfig {
|
|
2
|
+
name: string;
|
|
3
|
+
compatibilityDate?: string;
|
|
4
|
+
resources?: {
|
|
5
|
+
db?: {
|
|
6
|
+
type: "d1";
|
|
7
|
+
name: string;
|
|
8
|
+
database_id: string;
|
|
9
|
+
};
|
|
10
|
+
cache?: {
|
|
11
|
+
type: "kv";
|
|
12
|
+
name: string;
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
storage?: {
|
|
16
|
+
type: "r2";
|
|
17
|
+
name: string;
|
|
18
|
+
};
|
|
19
|
+
queue?: {
|
|
20
|
+
type: "queue";
|
|
21
|
+
name: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
actors?: string[];
|
|
25
|
+
rooms?: string[];
|
|
26
|
+
workflows?: string[];
|
|
27
|
+
env?: Record<string, string>;
|
|
28
|
+
}
|
|
29
|
+
export declare function defineConfig(config: FlareflowConfig): FlareflowConfig;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE;QACV,EAAE,CAAC,EAAE;YAAE,IAAI,EAAE,IAAI,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;QACvD,KAAK,CAAC,EAAE;YAAE,IAAI,EAAE,IAAI,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC;QACjD,OAAO,CAAC,EAAE;YAAE,IAAI,EAAE,IAAI,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QACvC,KAAK,CAAC,EAAE;YAAE,IAAI,EAAE,OAAO,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;KACzC,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAErE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,MAAM,UAAU,YAAY,CAAC,MAAuB;IAClD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/studio.html
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Flareflow Studio</title>
|
|
8
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
9
|
+
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
10
|
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
11
|
+
<script src="https://unpkg.com/lucide-react"></script>
|
|
12
|
+
<style>
|
|
13
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
14
|
+
|
|
15
|
+
body {
|
|
16
|
+
font-family: 'Inter', sans-serif;
|
|
17
|
+
background-color: #0a0a0a;
|
|
18
|
+
color: #f4f4f4;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.glass {
|
|
22
|
+
background: rgba(255, 255, 255, 0.03);
|
|
23
|
+
backdrop-filter: blur(8px);
|
|
24
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
27
|
+
</head>
|
|
28
|
+
|
|
29
|
+
<body>
|
|
30
|
+
<div id="root"></div>
|
|
31
|
+
<script>
|
|
32
|
+
const { useState, useEffect } = React;
|
|
33
|
+
const { Home, Database, Box, Play, MessageCircle, Settings, ChevronRight, Activity, Search } = LucideReact;
|
|
34
|
+
|
|
35
|
+
const API_BASE = 'http://localhost:8787/__Flareflow/studio';
|
|
36
|
+
|
|
37
|
+
function App() {
|
|
38
|
+
const [activeTab, setActiveTab] = useState('dashboard');
|
|
39
|
+
const [meta, setMeta] = useState(null);
|
|
40
|
+
const [loading, setLoading] = useState(true);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
fetch(`${API_BASE}/meta`)
|
|
44
|
+
.then(r => r.json())
|
|
45
|
+
.then(data => { setMeta(data); setLoading(false); })
|
|
46
|
+
.catch(e => { console.error(e); setLoading(false); });
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
if (loading) return (
|
|
50
|
+
<div className="flex h-screen items-center justify-center">
|
|
51
|
+
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-indigo-500"></div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="flex h-screen overflow-hidden">
|
|
57
|
+
{/* Sidebar */}
|
|
58
|
+
<div className="w-64 border-r border-white/5 bg-[#0e0e0e] flex flex-col">
|
|
59
|
+
<div className="p-6 flex items-center gap-2 mb-4">
|
|
60
|
+
<div className="bg-indigo-600 rounded-lg p-1">
|
|
61
|
+
<Box size={20} className="text-white" />
|
|
62
|
+
</div>
|
|
63
|
+
<span className="font-bold text-lg tracking-tight">Flareflow Studio</span>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<nav className="flex-1 px-4 space-y-1">
|
|
67
|
+
<NavItem icon={<Home size={18} />} label="Dashboard" active={activeTab === 'dashboard'} onClick={() => setActiveTab('dashboard')} />
|
|
68
|
+
<NavItem icon={<Database size={18} />} label="Entities" active={activeTab === 'entities'} onClick={() => setActiveTab('entities')} count={meta?.entities.length} />
|
|
69
|
+
<NavItem icon={<Box size={18} />} label="Actors" active={activeTab === 'actors'} onClick={() => setActiveTab('actors')} count={meta?.actors.length} />
|
|
70
|
+
<NavItem icon={<Play size={18} />} label="Workflows" active={activeTab === 'workflows'} onClick={() => setActiveTab('workflows')} count={meta?.workflows.length} />
|
|
71
|
+
<NavItem icon={<MessageCircle size={18} />} label="Realtime" active={activeTab === 'realtime'} onClick={() => setActiveTab('realtime')} count={meta?.rooms.length} />
|
|
72
|
+
</nav>
|
|
73
|
+
|
|
74
|
+
<div className="p-6 border-t border-white/5 text-xs text-white/40">
|
|
75
|
+
v0.0.1 ALPHA
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
{/* Content */}
|
|
80
|
+
<div className="flex-1 overflow-auto bg-[#0a0a0a]">
|
|
81
|
+
<header className="h-16 border-b border-white/5 flex items-center justify-between px-8 bg-[#0a0a0a]/80 backdrop-blur-md sticky top-0 z-10">
|
|
82
|
+
<h2 className="font-semibold text-lg">{activeTab.charAt(0).toUpperCase() + activeTab.slice(1)}</h2>
|
|
83
|
+
<div className="flex items-center gap-4">
|
|
84
|
+
<div className="flex items-center gap-2 text-xs text-indigo-400 bg-indigo-500/10 px-3 py-1.5 rounded-full border border-indigo-500/20">
|
|
85
|
+
<Activity size={12} /> Live Sync
|
|
86
|
+
</div>
|
|
87
|
+
<div className="text-xs text-white/40 font-mono">localhost:8787</div>
|
|
88
|
+
</div>
|
|
89
|
+
</header>
|
|
90
|
+
|
|
91
|
+
<main className="p-8">
|
|
92
|
+
{activeTab === 'dashboard' && <DashboardView meta={meta} />}
|
|
93
|
+
{activeTab === 'entities' && <EntitiesView meta={meta} />}
|
|
94
|
+
{activeTab === 'actors' && <ActorsView meta={meta} />}
|
|
95
|
+
</main>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function NavItem({ icon, label, active, onClick, count }) {
|
|
102
|
+
return (
|
|
103
|
+
<button
|
|
104
|
+
onClick={onClick}
|
|
105
|
+
className={`w-full flex items-center justify-between px-4 py-2.5 rounded-lg transition-all ${active ? 'bg-indigo-600/10 text-indigo-400 border border-indigo-500/20' : 'text-white/60 hover:text-white hover:bg-white/5 border border-transparent'}`}
|
|
106
|
+
>
|
|
107
|
+
<div className="flex items-center gap-3">
|
|
108
|
+
{icon}
|
|
109
|
+
<span className="font-medium text-sm">{label}</span>
|
|
110
|
+
</div>
|
|
111
|
+
{count !== undefined && <span className="text-[10px] bg-white/5 px-2 py-0.5 rounded-full border border-white/5">{count}</span>}
|
|
112
|
+
</button>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function DashboardView({ meta }) {
|
|
117
|
+
return (
|
|
118
|
+
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-2 duration-500">
|
|
119
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
120
|
+
<StatCard label="D1 Entities" value={meta.entities.length} icon={<Database size={20} />} color="indigo" />
|
|
121
|
+
<StatCard label="Business Actors" value={meta.actors.length} icon={<Box size={20} />} color="emerald" />
|
|
122
|
+
<StatCard label="Active Workflows" value={meta.workflows.length} icon={<Play size={20} />} color="amber" />
|
|
123
|
+
<StatCard label="Realtime Rooms" value={meta.rooms.length} icon={<MessageCircle size={20} />} color="rose" />
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div className="glass rounded-xl p-8">
|
|
127
|
+
<h3 className="text-xl font-bold mb-4">Welcome to Flareflow Studio</h3>
|
|
128
|
+
<p className="text-white/60 max-w-2xl leading-relaxed">
|
|
129
|
+
Your application <span className="text-indigo-400 font-mono">"{meta.name}"</span> is running locally.
|
|
130
|
+
Use the sidebar to explore your database tables, inspect Durable Object state, and monitor workflow execution history in real-time.
|
|
131
|
+
</p>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function StatCard({ label, value, icon, color }) {
|
|
138
|
+
const colors = {
|
|
139
|
+
indigo: 'text-indigo-400 bg-indigo-500/10 border-indigo-500/20',
|
|
140
|
+
emerald: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20',
|
|
141
|
+
amber: 'text-amber-400 bg-amber-500/10 border-amber-500/20',
|
|
142
|
+
rose: 'text-rose-400 bg-rose-500/10 border-rose-500/20'
|
|
143
|
+
};
|
|
144
|
+
return (
|
|
145
|
+
<div className="glass rounded-xl p-6 flex items-center gap-6">
|
|
146
|
+
<div className={`p-3 rounded-xl border ${colors[color]}`}>{icon}</div>
|
|
147
|
+
<div>
|
|
148
|
+
<div className="text-white/40 text-sm font-medium">{label}</div>
|
|
149
|
+
<div className="text-2xl font-bold">{value}</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function EntitiesView({ meta }) {
|
|
156
|
+
const [selectedTable, setSelectedTable] = useState(meta.entities[0]);
|
|
157
|
+
const [rows, setRows] = useState([]);
|
|
158
|
+
const [loading, setLoading] = useState(false);
|
|
159
|
+
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (!selectedTable) return;
|
|
162
|
+
setLoading(true);
|
|
163
|
+
fetch(`${API_BASE}/db/${selectedTable}`)
|
|
164
|
+
.then(r => r.json())
|
|
165
|
+
.then(data => { setRows(data); setLoading(false); })
|
|
166
|
+
.catch(e => setLoading(false));
|
|
167
|
+
}, [selectedTable]);
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<div className="space-y-6">
|
|
171
|
+
<div className="flex gap-2">
|
|
172
|
+
{meta.entities.map(t => (
|
|
173
|
+
<button
|
|
174
|
+
key={t}
|
|
175
|
+
onClick={() => setSelectedTable(t)}
|
|
176
|
+
className={`px-4 py-2 rounded-lg text-sm font-medium border transition-all ${selectedTable === t ? 'bg-white/5 border-white/20 text-white' : 'border-transparent text-white/40 hover:text-white/60'}`}
|
|
177
|
+
>
|
|
178
|
+
{t}
|
|
179
|
+
</button>
|
|
180
|
+
))}
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div className="glass rounded-xl overflow-hidden">
|
|
184
|
+
{loading ? (
|
|
185
|
+
<div className="p-20 flex justify-center"><div className="animate-spin rounded-full h-8 w-8 border-t-2 border-indigo-500"></div></div>
|
|
186
|
+
) : (
|
|
187
|
+
<div className="overflow-x-auto">
|
|
188
|
+
<table className="w-full text-left text-sm">
|
|
189
|
+
<thead>
|
|
190
|
+
<tr className="border-b border-white/5 bg-white/[0.02]">
|
|
191
|
+
{rows.length > 0 && Object.keys(rows[0]).map(k => (
|
|
192
|
+
<th key={k} className="px-6 py-4 font-semibold text-white/40">{k}</th>
|
|
193
|
+
))}
|
|
194
|
+
</tr>
|
|
195
|
+
</thead>
|
|
196
|
+
<tbody className="divide-y divide-white/5">
|
|
197
|
+
{rows.map((r, i) => (
|
|
198
|
+
<tr key={i} className="hover:bg-white/[0.01] transition-colors">
|
|
199
|
+
{Object.values(r).map((v, j) => (
|
|
200
|
+
<td key={j} className="px-6 py-4 text-white/80 font-mono text-xs">
|
|
201
|
+
{typeof v === 'object' ? JSON.stringify(v) : String(v)}
|
|
202
|
+
</td>
|
|
203
|
+
))}
|
|
204
|
+
</tr>
|
|
205
|
+
))}
|
|
206
|
+
</tbody>
|
|
207
|
+
</table>
|
|
208
|
+
{rows.length === 0 && <div className="p-20 text-center text-white/20 italic">No rows found</div>}
|
|
209
|
+
</div>
|
|
210
|
+
)}
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function ActorsView({ meta }) {
|
|
217
|
+
const [selectedType, setSelectedType] = useState(meta.actors[0]);
|
|
218
|
+
const [actorId, setActorId] = useState('');
|
|
219
|
+
const [state, setState] = useState(null);
|
|
220
|
+
const [loading, setLoading] = useState(false);
|
|
221
|
+
|
|
222
|
+
const fetchState = () => {
|
|
223
|
+
if (!actorId) return;
|
|
224
|
+
setLoading(true);
|
|
225
|
+
fetch(`${API_BASE}/actor/${selectedType}/${actorId}`)
|
|
226
|
+
.then(r => r.json())
|
|
227
|
+
.then(data => { setState(data.state); setLoading(false); })
|
|
228
|
+
.catch(e => setLoading(false));
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
<div className="space-y-8">
|
|
233
|
+
<div className="max-w-xl space-y-4">
|
|
234
|
+
<div className="text-sm font-medium text-white/40 mb-1">Actor ID (Name)</div>
|
|
235
|
+
<div className="flex gap-3">
|
|
236
|
+
<select
|
|
237
|
+
value={selectedType}
|
|
238
|
+
onChange={e => setSelectedType(e.target.value)}
|
|
239
|
+
className="bg-[#0e0e0e] border border-white/10 rounded-lg px-4 py-2 text-sm focus:outline-none focus:border-indigo-500"
|
|
240
|
+
>
|
|
241
|
+
{meta.actors.map(t => <option key={t} value={t}>{t}</option>)}
|
|
242
|
+
</select>
|
|
243
|
+
<input
|
|
244
|
+
type="text"
|
|
245
|
+
placeholder="Enter instance ID..."
|
|
246
|
+
value={actorId}
|
|
247
|
+
onChange={e => setActorId(e.target.value)}
|
|
248
|
+
className="flex-1 bg-[#0e0e0e] border border-white/10 rounded-lg px-4 py-2 text-sm focus:outline-none focus:border-indigo-500"
|
|
249
|
+
/>
|
|
250
|
+
<button
|
|
251
|
+
onClick={fetchState}
|
|
252
|
+
className="bg-indigo-600 hover:bg-indigo-500 text-white px-6 py-2 rounded-lg text-sm font-semibold transition-all shadow-lg shadow-indigo-600/20"
|
|
253
|
+
>
|
|
254
|
+
Inspect
|
|
255
|
+
</button>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<div className="glass rounded-xl p-8 min-h-[400px]">
|
|
260
|
+
{loading ? (
|
|
261
|
+
<div className="flex justify-center py-20"><div className="animate-spin rounded-full h-8 w-8 border-t-2 border-indigo-500"></div></div>
|
|
262
|
+
) : state ? (
|
|
263
|
+
<div className="space-y-4 animate-in fade-in duration-300">
|
|
264
|
+
<div className="flex items-center gap-2 mb-2">
|
|
265
|
+
<div className="h-2 w-2 rounded-full bg-emerald-500 animate-pulse"></div>
|
|
266
|
+
<span className="text-xs font-bold text-white/40 uppercase tracking-widest">Active State</span>
|
|
267
|
+
</div>
|
|
268
|
+
<pre className="text-indigo-300 font-mono text-xs leading-relaxed bg-black/40 p-6 rounded-lg border border-white/5 overflow-auto max-h-[500px]">
|
|
269
|
+
{JSON.stringify(state, null, 2)}
|
|
270
|
+
</pre>
|
|
271
|
+
</div>
|
|
272
|
+
) : (
|
|
273
|
+
<div className="flex flex-col items-center justify-center py-20 text-white/20">
|
|
274
|
+
<Search size={48} className="mb-4 opacity-50" />
|
|
275
|
+
<p className="italic">Enter an Actor ID above to inspect its live state</p>
|
|
276
|
+
</div>
|
|
277
|
+
)}
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
284
|
+
root.render(<App />);
|
|
285
|
+
</script>
|
|
286
|
+
</body>
|
|
287
|
+
|
|
288
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flareflow/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "flareflow CLI",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"flareflow": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc && cp src/studio.html dist/studio.html",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"cac": "^6.7.14",
|
|
16
|
+
"esbuild": "^0.20.2"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"typescript": "^5.4.5",
|
|
20
|
+
"@types/node": "^20.0.0"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/flareflow/flareflow"
|
|
32
|
+
}
|
|
33
|
+
}
|