free-framework 4.4.1 β†’ 4.5.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/README.md CHANGED
@@ -110,7 +110,13 @@ error 404 {
110
110
  | `free serve` | Start the development server |
111
111
  | `free build` | Build for production (minified) |
112
112
  | `free devtools` | Launch the DevTools Dashboard |
113
- | `free test` | Run the test suite with Vitest |
113
+ | `free doctor` | Run project health diagnostics |
114
+ | `free update` | Update Free Framework to the latest version |
115
+ | `free route:list` | List all registered routes |
116
+ | `free db:seed` | Run database seeders |
117
+ | `free make:crud <name>` | Scaffold full CRUD for a model |
118
+ | `free shell` | Enter interactive Free Shell |
119
+ | `free bench` | Benchmark current app performance |
114
120
  | `free install <pkg>` | Install ecosystem plugins |
115
121
  | `free make:model <name>` | Scaffold a new model |
116
122
 
package/bin/free.js CHANGED
@@ -117,4 +117,55 @@ program
117
117
  require("../cli/commands/doctor")();
118
118
  });
119
119
 
120
+ program
121
+ .command("update")
122
+ .description("Update Free Framework to the latest version")
123
+ .action(() => {
124
+ require("../cli/commands/update")();
125
+ });
126
+
127
+ program
128
+ .command("route:list")
129
+ .description("List all registered routes")
130
+ .action(() => {
131
+ require("../cli/commands/route_list")();
132
+ });
133
+
134
+ program
135
+ .command("db:seed")
136
+ .description("Run database seeders")
137
+ .action(() => {
138
+ require("../cli/commands/db_seed")();
139
+ });
140
+
141
+ program
142
+ .command("make:crud <name>")
143
+ .description("Scaffold full CRUD for a model")
144
+ .action((name) => {
145
+ require("../cli/commands/make_crud")(name);
146
+ });
147
+
148
+ program
149
+ .command("shell")
150
+ .description("Enter interactive Free Shell")
151
+ .action(() => {
152
+ require("../cli/commands/shell")();
153
+ });
154
+
155
+ program
156
+ .command("bench")
157
+ .description("Benchmark current app performance")
158
+ .option("-u, --url <url>", "URL to benchmark", "http://localhost:3000")
159
+ .option("-d, --duration <duration>", "Duration in seconds", 5)
160
+ .action((options) => {
161
+ require("../cli/commands/bench")(options);
162
+ });
163
+
164
+ program
165
+ .command("update")
166
+ .description("Update Free Framework to the latest version")
167
+ .action(() => {
168
+ require("../cli/commands/update")();
169
+ });
170
+
120
171
  program.parse(process.argv);
@@ -0,0 +1,45 @@
1
+ /**
2
+ * cli/commands/bench.js
3
+ * Simple performance benchmarking tool.
4
+ */
5
+
6
+ const http = require("http");
7
+
8
+ module.exports = function (options) {
9
+ const url = options.url || "http://localhost:3000";
10
+ const duration = options.duration || 5; // seconds
11
+ console.log(`\nπŸ“Š Benchmarking ${url} for ${duration}s...\n`);
12
+
13
+ let requests = 0;
14
+ let errors = 0;
15
+ const startTime = Date.now();
16
+ const endTime = startTime + (duration * 1000);
17
+
18
+ const runBench = () => {
19
+ if (Date.now() >= endTime) {
20
+ const totalTime = (Date.now() - startTime) / 1000;
21
+ const rps = (requests / totalTime).toFixed(2);
22
+ console.log(`\n🏁 Results:`);
23
+ console.log(` Total Requests: ${requests}`);
24
+ console.log(` Total Errors: ${errors}`);
25
+ console.log(` RPS: ${rps} req/s\n`);
26
+ return;
27
+ }
28
+
29
+ const req = http.get(url, (res) => {
30
+ requests++;
31
+ res.on('data', () => { });
32
+ res.on('end', runBench);
33
+ });
34
+
35
+ req.on('error', (e) => {
36
+ errors++;
37
+ runBench();
38
+ });
39
+ };
40
+
41
+ // Parallelize with multiple concurrent workers
42
+ for (let i = 0; i < 10; i++) {
43
+ runBench();
44
+ }
45
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * cli/commands/db_seed.js
3
+ * Runs database seeders to populate the environment.
4
+ */
5
+
6
+ const fs = require("fs-extra");
7
+ const path = require("path");
8
+
9
+ module.exports = async function () {
10
+ console.log("\n🌱 Free Framework - Seeding Database...\n");
11
+
12
+ const seederDir = path.join(process.cwd(), "database/seeders");
13
+ if (!fs.existsSync(seederDir)) {
14
+ console.log("❌ No database/seeders directory found.");
15
+ return;
16
+ }
17
+
18
+ const files = fs.readdirSync(seederDir).filter(f => f.endsWith(".js"));
19
+ if (files.length === 0) {
20
+ console.log("ℹ️ No seeder files found.");
21
+ return;
22
+ }
23
+
24
+ try {
25
+ const { ORM } = require("../../database/orm");
26
+ const knex = new ORM().knex;
27
+
28
+ for (const file of files) {
29
+ console.log(` πŸ”Έ Running seeder: ${file}...`);
30
+ const seeder = require(path.join(seederDir, file));
31
+ await seeder.run(knex);
32
+ }
33
+
34
+ console.log("\nβœ… Database seeding completed successfully.");
35
+ } catch (err) {
36
+ console.error("\n❌ Seeding failed:", err.message);
37
+ }
38
+ console.log("");
39
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * cli/commands/make_crud.js
3
+ * Scaffolds Model, Controller, Views, and Routes for a resource.
4
+ */
5
+
6
+ const fs = require("fs-extra");
7
+ const path = require("path");
8
+
9
+ module.exports = function (name) {
10
+ const plural = name.toLowerCase() + "s";
11
+ const singular = name.toLowerCase();
12
+ const className = name.charAt(0) + name.slice(1);
13
+
14
+ console.log(`\nπŸ—οΈ Scaffolding CRUD for resource: ${className}\n`);
15
+
16
+ // 1. Create Model
17
+ const modelPath = path.join(process.cwd(), `app/models/${className}.free`);
18
+ if (!fs.existsSync(modelPath)) {
19
+ const modelContent = `model ${className} {
20
+ title string
21
+ content text
22
+ status string
23
+ }\n`;
24
+ fs.ensureDirSync(path.dirname(modelPath));
25
+ fs.writeFileSync(modelPath, modelContent);
26
+ console.log(` βœ… Created Model: app/models/${className}.free`);
27
+ }
28
+
29
+ // 2. Create Controller
30
+ const controllerPath = path.join(process.cwd(), `app/Http/Controllers/${className}Controller.free`);
31
+ const controllerContent = `// ${className} Controller
32
+ action list${plural} {
33
+ const items = await ${className}.all();
34
+ return items;
35
+ }
36
+
37
+ action get${className} {
38
+ const item = await ${className}.find(params.id);
39
+ return item;
40
+ }
41
+
42
+ action create${className} {
43
+ const item = await ${className}.create(body);
44
+ return item;
45
+ }
46
+
47
+ action delete${className} {
48
+ await ${className}.delete(params.id);
49
+ return { success: true };
50
+ }
51
+ `;
52
+ fs.ensureDirSync(path.dirname(controllerPath));
53
+ fs.writeFileSync(controllerPath, controllerContent);
54
+ console.log(` βœ… Created Controller: app/Http/Controllers/${className}Controller.free`);
55
+
56
+ // 3. Create Basic View
57
+ const viewPath = path.join(process.cwd(), `resources/views/${plural}.free`);
58
+ const viewContent = `page ${plural} {
59
+ h1 { text "Manage ${plural}" }
60
+
61
+ component ${className}List {
62
+ onMount {
63
+ state.items = await actions.list${plural}()
64
+ }
65
+
66
+ loop item in items {
67
+ div {
68
+ h3 { text item.title }
69
+ p { text item.content }
70
+ button {
71
+ text "Delete"
72
+ on-click {
73
+ await actions.delete${className}(item.id)
74
+ state.items = await actions.list${plural}()
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ `;
82
+ fs.ensureDirSync(path.dirname(viewPath));
83
+ fs.writeFileSync(viewPath, viewContent);
84
+ console.log(` βœ… Created View: resources/views/${plural}.free`);
85
+
86
+ console.log(`\nπŸš€ CRUD scaffolded! Don't forget to register the routes in app/routes.free\n`);
87
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * cli/commands/route_list.js
3
+ * Displays a table of all registered routes in the project.
4
+ */
5
+
6
+ const fs = require("fs-extra");
7
+ const path = require("path");
8
+ const { tokenize } = require("../../compiler/lexer");
9
+ const { parse } = require("../../compiler/parser");
10
+
11
+ module.exports = function () {
12
+ console.log("\nπŸ—ΊοΈ Free Framework - Route Registry\n");
13
+
14
+ const baseDir = process.cwd();
15
+ const routesFile = path.join(baseDir, "app/routes.free");
16
+
17
+ // Check multiple possible locations for routes
18
+ const paths = [
19
+ path.join(baseDir, "app/routes.free"),
20
+ path.join(baseDir, "routes/web.free"),
21
+ path.join(baseDir, "routes/api.free")
22
+ ];
23
+
24
+ const allRoutes = [];
25
+
26
+ paths.forEach(p => {
27
+ if (fs.existsSync(p)) {
28
+ try {
29
+ const code = fs.readFileSync(p, "utf8");
30
+ const tokens = tokenize(code, path.basename(p));
31
+ const ast = parse(tokens, path.basename(p), code);
32
+
33
+ ast.filter(n => n.type === 'route').forEach(r => {
34
+ allRoutes.push({
35
+ method: r.method.toUpperCase(),
36
+ path: r.path,
37
+ handler: r.body.some(b => b.type === 'view') ? `View: ${r.body.find(b => b.type === 'view').viewName}` : 'Action/Logic',
38
+ file: path.basename(p)
39
+ });
40
+ });
41
+ } catch (e) { }
42
+ }
43
+ });
44
+
45
+ if (allRoutes.length === 0) {
46
+ console.log("ℹ️ No routes found in project.");
47
+ } else {
48
+ console.table(allRoutes);
49
+ }
50
+ console.log("");
51
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * cli/commands/shell.js
3
+ * Interactive REPL for Free Framework.
4
+ */
5
+
6
+ const repl = require("repl");
7
+ const path = require("path");
8
+ const fs = require("fs");
9
+
10
+ module.exports = function () {
11
+ console.log("\n🐚 Free Framework Interactive Shell");
12
+ console.log("Type '.exit' to leave. Global 'Model' and project models are available.\n");
13
+
14
+ const r = repl.start({
15
+ prompt: "free > ",
16
+ useGlobal: true
17
+ });
18
+
19
+ // Auto-load project context
20
+ try {
21
+ const { ORM } = require("../../database/orm");
22
+ const { Model } = require("../../database/model");
23
+
24
+ r.context.ORM = ORM;
25
+ r.context.Model = Model;
26
+ r.context.db = new ORM().knex;
27
+
28
+ // Try to load models from app/models
29
+ const modelDir = path.join(process.cwd(), "app/models");
30
+ if (fs.existsSync(modelDir)) {
31
+ fs.readdirSync(modelDir).forEach(file => {
32
+ if (file.endsWith(".free")) {
33
+ const modelName = path.basename(file, ".free");
34
+ // In a real scenario, we'd need to require the compiled JS version
35
+ // For now we expose the base Model class Proxy logic
36
+ r.context[modelName] = Model.extend(modelName);
37
+ }
38
+ });
39
+ }
40
+ } catch (e) {
41
+ console.log("⚠️ Context partial load: " + e.message);
42
+ }
43
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * cli/commands/update.js
3
+ * Implementation of 'free update' command to keep the framework current.
4
+ */
5
+
6
+ const { execSync } = require("child_process");
7
+ const pkg = require("../../package.json");
8
+
9
+ module.exports = function () {
10
+ console.log("\nπŸ”„ Checking for Free Framework updates...\n");
11
+ console.log(`Current version: ${pkg.version}`);
12
+
13
+ try {
14
+ console.log("πŸ“‘ Fetching latest version from NPM...");
15
+ // Use npm view to get the latest version
16
+ const latest = execSync("npm view free-framework version").toString().trim();
17
+
18
+ if (latest === pkg.version) {
19
+ console.log("βœ… You are already using the latest version.");
20
+ } else {
21
+ console.log(`πŸš€ New version available: ${latest}`);
22
+ console.log("πŸ› οΈ Updating global Free Framework CLI...");
23
+
24
+ // Run global install
25
+ execSync("npm install -g free-framework@latest", { stdio: "inherit" });
26
+
27
+ console.log("\nβœ… Update complete! Enjoy the new features.");
28
+ }
29
+ } catch (err) {
30
+ console.error("\n❌ Update failed. Please ensure you have an internet connection and proper permissions.");
31
+ console.log("πŸ’‘ Try running: sudo npm install -g free-framework@latest");
32
+ }
33
+ console.log("");
34
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-framework",
3
- "version": "4.4.1",
3
+ "version": "4.5.0",
4
4
  "description": "Professional Node.js engine for the .free language. Blazing-fast SSR, Islands Architecture, and built-in ORM.",
5
5
  "main": "index.js",
6
6
  "bin": {