free-framework 4.4.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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/bin/free.js +118 -0
  4. package/cli/commands/build.js +124 -0
  5. package/cli/commands/deploy.js +143 -0
  6. package/cli/commands/devtools.js +210 -0
  7. package/cli/commands/doctor.js +72 -0
  8. package/cli/commands/install.js +28 -0
  9. package/cli/commands/make.js +74 -0
  10. package/cli/commands/migrate.js +67 -0
  11. package/cli/commands/new.js +54 -0
  12. package/cli/commands/serve.js +73 -0
  13. package/cli/commands/test.js +57 -0
  14. package/compiler/analyzer.js +102 -0
  15. package/compiler/generator.js +386 -0
  16. package/compiler/lexer.js +166 -0
  17. package/compiler/parser.js +410 -0
  18. package/database/model.js +6 -0
  19. package/database/orm.js +379 -0
  20. package/database/query-builder.js +179 -0
  21. package/index.js +51 -0
  22. package/package.json +80 -0
  23. package/plugins/auth.js +212 -0
  24. package/plugins/cache.js +85 -0
  25. package/plugins/chat.js +32 -0
  26. package/plugins/mail.js +53 -0
  27. package/plugins/metrics.js +126 -0
  28. package/plugins/payments.js +59 -0
  29. package/plugins/queue.js +139 -0
  30. package/plugins/search.js +51 -0
  31. package/plugins/storage.js +123 -0
  32. package/plugins/upload.js +62 -0
  33. package/router/router.js +57 -0
  34. package/runtime/app.js +14 -0
  35. package/runtime/client.js +254 -0
  36. package/runtime/cluster.js +35 -0
  37. package/runtime/edge.js +62 -0
  38. package/runtime/middleware/maintenance.js +54 -0
  39. package/runtime/middleware/security.js +30 -0
  40. package/runtime/server.js +130 -0
  41. package/runtime/validator.js +102 -0
  42. package/runtime/views/error.free +104 -0
  43. package/runtime/views/maintenance.free +0 -0
  44. package/template-engine/renderer.js +24 -0
  45. package/templates/app-template/.env +23 -0
  46. package/templates/app-template/app/Exceptions/Handler.js +65 -0
  47. package/templates/app-template/app/Http/Controllers/AuthController.free +91 -0
  48. package/templates/app-template/app/Http/Middleware/AuthGuard.js +46 -0
  49. package/templates/app-template/app/Services/Storage.js +75 -0
  50. package/templates/app-template/app/Services/Validator.js +91 -0
  51. package/templates/app-template/app/controllers/AuthController.free +42 -0
  52. package/templates/app-template/app/middleware/auth.js +25 -0
  53. package/templates/app-template/app/models/User.free +32 -0
  54. package/templates/app-template/app/routes.free +12 -0
  55. package/templates/app-template/app/styles.css +23 -0
  56. package/templates/app-template/app/views/counter.free +23 -0
  57. package/templates/app-template/app/views/header.free +13 -0
  58. package/templates/app-template/config/app.js +32 -0
  59. package/templates/app-template/config/auth.js +39 -0
  60. package/templates/app-template/config/database.js +54 -0
  61. package/templates/app-template/package.json +28 -0
  62. package/templates/app-template/resources/css/app.css +11 -0
  63. package/templates/app-template/resources/views/dashboard.free +25 -0
  64. package/templates/app-template/resources/views/home.free +26 -0
  65. package/templates/app-template/routes/api.free +22 -0
  66. package/templates/app-template/routes/web.free +25 -0
  67. package/templates/app-template/tailwind.config.js +21 -0
  68. package/templates/app-template/views/about.ejs +47 -0
  69. package/templates/app-template/views/home.ejs +52 -0
  70. package/templates/auth/login.html +144 -0
  71. package/templates/auth/register.html +146 -0
  72. package/utils/logger.js +20 -0
@@ -0,0 +1,379 @@
1
+ /**
2
+ * database/orm.js
3
+ * Production-grade Active Record ORM powered by Knex.
4
+ * Supports: SQLite, PostgreSQL, MySQL.
5
+ * Features: Relations, Transactions, Pagination, Eager Loading, Soft Deletes.
6
+ */
7
+
8
+ const knex = require('knex');
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+
12
+ // Extend Knex QueryBuilder with .get() for Laravel-like syntax
13
+ const tempKnex = knex({ client: 'sqlite3' });
14
+ const KnexQueryBuilder = tempKnex.queryBuilder().constructor;
15
+ KnexQueryBuilder.prototype.get = function () {
16
+ return this.select();
17
+ };
18
+ class ORM {
19
+ static connect() {
20
+ if (!this.db) {
21
+ const client = process.env.DB_CLIENT || 'mysql2';
22
+ let connection = {};
23
+
24
+ if (client === 'mysql2' || client === 'pg') {
25
+ connection = {
26
+ host: process.env.DB_HOST || '127.0.0.1',
27
+ port: process.env.DB_PORT || (client === 'pg' ? 5432 : 3306),
28
+ user: process.env.DB_USER || (client === 'pg' ? 'postgres' : 'root'),
29
+ password: process.env.DB_PASS || '',
30
+ database: process.env.DB_NAME || 'free_db',
31
+ };
32
+ if (client === 'pg' && process.env.DB_SSL === 'true') {
33
+ connection.ssl = { rejectUnauthorized: false };
34
+ }
35
+ } else {
36
+ // SQLite fallback
37
+ const dbPath = process.env.DB_PATH || 'database.sqlite';
38
+ const fullPath = path.isAbsolute(dbPath) ? dbPath : path.join(process.cwd(), dbPath);
39
+ connection = { filename: fullPath };
40
+
41
+ const dbDir = path.dirname(fullPath);
42
+ if (!fs.existsSync(dbDir)) {
43
+ fs.mkdirSync(dbDir, { recursive: true });
44
+ }
45
+ }
46
+
47
+ this.db = knex({
48
+ client,
49
+ connection,
50
+ useNullAsDefault: client === 'sqlite3',
51
+ acquireConnectionTimeout: 10000,
52
+ pool: {
53
+ min: parseInt(process.env.DB_POOL_MIN || '0'),
54
+ max: parseInt(process.env.DB_POOL_MAX || '10'),
55
+ }
56
+ });
57
+
58
+ console.log(`🗄️ ORM connected. Driver: ${client} (Pool: ${process.env.DB_POOL_MIN || 0}-${process.env.DB_POOL_MAX || 10})`);
59
+ }
60
+ return this.db;
61
+ }
62
+
63
+ /**
64
+ * Auto-migrate all registered models.
65
+ * Supports: string, number, boolean, text, float, json fields.
66
+ * Supports: index declarations.
67
+ */
68
+ static async migrate(models) {
69
+ const db = this.connect();
70
+
71
+ try {
72
+ for (const [name, model] of Object.entries(models)) {
73
+ const tableName = name.toLowerCase() + 's';
74
+ const exists = await db.schema.hasTable(tableName);
75
+
76
+ if (!exists) {
77
+ await db.schema.createTable(tableName, table => {
78
+ table.increments('id').primary();
79
+
80
+ if (model.fields) {
81
+ model.fields.forEach(field => {
82
+ if (!field.name || field.name === 'id' || field.name === 'created_at' || field.name === 'updated_at' || field.name === 'timestamps' || field.type === '}' || field.name === '}') return;
83
+ let col;
84
+ if (field.type === 'string') col = table.string(field.name);
85
+ else if (field.type === 'integer') col = table.integer(field.name);
86
+ else if (field.type === 'number') col = table.integer(field.name).defaultTo(0);
87
+ else if (field.type === 'boolean') col = table.boolean(field.name).defaultTo(false);
88
+ else if (field.type === 'text') col = table.text(field.name);
89
+ else if (field.type === 'float') col = table.float(field.name).defaultTo(0);
90
+ else if (field.type === 'json') col = table.json(field.name);
91
+ else if (field.type === 'datetime') col = table.timestamp(field.name);
92
+ else col = table.string(field.name);
93
+
94
+ if (field.unique) col.unique();
95
+ if (field.index) col.index();
96
+ if (field.default !== undefined) col.defaultTo(field.default);
97
+ });
98
+ }
99
+
100
+ if (model.softDeletes) {
101
+ table.timestamp('deleted_at').nullable();
102
+ }
103
+
104
+ table.timestamps(true, true);
105
+ });
106
+
107
+ if (model.indexes && model.indexes.length > 0) {
108
+ await db.schema.table(tableName, table => {
109
+ model.indexes.forEach(field => {
110
+ table.index(field, `idx_${tableName}_${field}`);
111
+ });
112
+ });
113
+ console.log(`🔍 Indexed: ${tableName} → [${model.indexes.join(', ')}]`);
114
+ }
115
+
116
+ console.log(`📦 Migrated: ${tableName}`);
117
+ }
118
+ }
119
+ } catch (err) {
120
+ console.error('❌ Migration Error:', err);
121
+ throw err;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Execute operations inside a single DB transaction.
127
+ * Auto-rollback on error.
128
+ */
129
+ static async transaction(callback) {
130
+ const db = this.connect();
131
+ return db.transaction(callback);
132
+ }
133
+ }
134
+
135
+
136
+
137
+ /**
138
+ * Base Model class with full Active Record methods.
139
+ */
140
+ class Model {
141
+ static table = null;
142
+ static fields = [];
143
+
144
+ static _tableName() {
145
+ return this.table || (this.name.toLowerCase() + 's');
146
+ }
147
+
148
+ static _db() {
149
+ return ORM.connect();
150
+ }
151
+
152
+ // ── Query Builders ───────────────────────────────────────────────────────
153
+
154
+ static query() {
155
+ return this._db()(this._tableName());
156
+ }
157
+
158
+ static all() {
159
+ return this.query();
160
+ }
161
+
162
+ static async find(id) {
163
+ return this._db()(this._tableName()).where({ id }).first();
164
+ }
165
+
166
+ static where(column, value) {
167
+ if (typeof column === 'object') {
168
+ return this._db()(this._tableName()).where(column);
169
+ }
170
+ return this._db()(this._tableName()).where(column, value);
171
+ }
172
+
173
+ static first(conditions) {
174
+ return this._db()(this._tableName()).where(conditions || {}).first();
175
+ }
176
+
177
+ /**
178
+ * Paginate results.
179
+ * @param {number} page - 1-based page number
180
+ * @param {number} limit - records per page
181
+ * @returns {{ data, total, page, lastPage }}
182
+ */
183
+ static async paginate(page = 1, limit = 20) {
184
+ const offset = (page - 1) * limit;
185
+ const db = this._db();
186
+ const tableName = this._tableName();
187
+
188
+ const [{ total }] = await db(tableName).count('id as total');
189
+ const data = await db(tableName).limit(limit).offset(offset);
190
+
191
+ return {
192
+ data,
193
+ total: Number(total),
194
+ page,
195
+ limit,
196
+ lastPage: Math.ceil(Number(total) / limit),
197
+ };
198
+ }
199
+
200
+ // ── Mutations ────────────────────────────────────────────────────────────
201
+
202
+ static async create(attributes) {
203
+ const [id] = await this._db()(this._tableName()).insert(attributes);
204
+ return this.find(id);
205
+ }
206
+
207
+ static async update(id, attributes) {
208
+ await this._db()(this._tableName()).where({ id }).update(attributes);
209
+ return this.find(id);
210
+ }
211
+
212
+ static async delete(id) {
213
+ return this._db()(this._tableName()).where({ id }).del();
214
+ }
215
+
216
+ /**
217
+ * hasMany relation.
218
+ * e.g. User.hasMany(Post, 'authorId', userId)
219
+ */
220
+ static async hasMany(RelatedModel, foreignKey, id) {
221
+ return ORM.connect()(RelatedModel._tableName()).where({ [foreignKey]: id });
222
+ }
223
+
224
+ /**
225
+ * belongsTo relation.
226
+ * e.g. Post.belongsTo(User, 'authorId', post.authorId)
227
+ */
228
+ static async belongsTo(ParentModel, foreignKey, foreignId) {
229
+ return ORM.connect()(ParentModel._tableName()).where({ id: foreignId }).first();
230
+ }
231
+
232
+ // ── Eager Loading ─────────────────────────────────────────────────────────
233
+
234
+ /**
235
+ * Fluent eager loading builder.
236
+ * @param {string | string[]} relations - e.g. "author" or ["author", "comments"]
237
+ * @returns {EagerBuilder}
238
+ */
239
+ static with(relations) {
240
+ return new EagerBuilder(this, Array.isArray(relations) ? relations : [relations]);
241
+ }
242
+
243
+ // ── Soft Deletes ──────────────────────────────────────────────────────────
244
+
245
+ static softDeletes = false;
246
+
247
+ /** Soft-delete: sets deleted_at timestamp instead of removing row. */
248
+ static async softDelete(id) {
249
+ return this._db()(this._tableName()).where({ id }).update({ deleted_at: new Date().toISOString() });
250
+ }
251
+
252
+ /** Restore a soft-deleted record. */
253
+ static async restore(id) {
254
+ return this._db()(this._tableName()).where({ id }).update({ deleted_at: null });
255
+ }
256
+
257
+ /** Query including soft-deleted rows. */
258
+ static withTrashed() {
259
+ return this._db()(this._tableName());
260
+ }
261
+
262
+ /** Query only soft-deleted rows. */
263
+ static onlyTrashed() {
264
+ return this._db()(this._tableName()).whereNotNull('deleted_at');
265
+ }
266
+
267
+ /** Override all() to exclude soft-deleted by default. */
268
+ static all() {
269
+ if (this.softDeletes) {
270
+ return this._db()(this._tableName()).whereNull('deleted_at');
271
+ }
272
+ return this._db()(this._tableName());
273
+ }
274
+ }
275
+
276
+ // ── EagerBuilder ──────────────────────────────────────────────────────────────
277
+
278
+ /**
279
+ * Fluent builder for eager-loading relations.
280
+ * Registered via Model.with("relation")
281
+ * Relations must be declared as static properties on the model:
282
+ * static relations = { author: { model: User, foreignKey: 'authorId', type: 'belongsTo' } }
283
+ */
284
+ class EagerBuilder {
285
+ constructor(ModelClass, relations) {
286
+ this.ModelClass = ModelClass;
287
+ this.relations = relations;
288
+ this._query = ModelClass._db()(ModelClass._tableName());
289
+ }
290
+
291
+ where(column, value) {
292
+ if (typeof column === 'object') {
293
+ this._query = this._query.where(column);
294
+ } else {
295
+ this._query = this._query.where(column, value);
296
+ }
297
+ return this;
298
+ }
299
+ limit(n) { this._query = this._query.limit(n); return this; }
300
+ offset(n) { this._query = this._query.offset(n); return this; }
301
+
302
+ async get() {
303
+ if (this.ModelClass.softDeletes) this._query = this._query.whereNull('deleted_at');
304
+ const rows = await this._query;
305
+ return this._loadRelations(rows);
306
+ }
307
+
308
+ async first() {
309
+ const rows = await this.get();
310
+ return rows[0] || null;
311
+ }
312
+
313
+ async paginate(page = 1, limit = 20) {
314
+ const offset = (page - 1) * limit;
315
+ this.limit(limit).offset(offset);
316
+ const data = await this.get();
317
+ const [{ total }] = await this.ModelClass._db()(this.ModelClass._tableName()).count('id as total');
318
+ return { data, total: Number(total), page, lastPage: Math.ceil(Number(total) / limit) };
319
+ }
320
+
321
+ async _loadRelations(rows) {
322
+ if (!rows || rows.length === 0) return rows;
323
+ const declared = this.ModelClass.relations || {};
324
+
325
+ // Group nested relations by their top-level key
326
+ // e.g. ["author.profile", "comments"] -> { author: ["profile"], comments: [] }
327
+ const groups = {};
328
+ for (const rel of this.relations) {
329
+ const [top, ...rest] = rel.split('.');
330
+ if (!groups[top]) groups[top] = [];
331
+ if (rest.length > 0) groups[top].push(rest.join('.'));
332
+ }
333
+
334
+ for (const [rel, nested] of Object.entries(groups)) {
335
+ const def = declared[rel];
336
+ if (!def) { console.warn(`[ORM] Unknown relation "${rel}" on ${this.ModelClass.name}`); continue; }
337
+ const { model: RelModel, foreignKey, type } = def;
338
+
339
+ if (type === 'hasMany') {
340
+ const ids = rows.map(r => r.id);
341
+ let query = RelModel.query().whereIn(foreignKey, ids);
342
+
343
+ // If nested relations exist, use with() on the related models
344
+ let related;
345
+ if (nested.length > 0) {
346
+ related = await RelModel.with(nested).whereIn(foreignKey, ids).get();
347
+ } else {
348
+ related = await query;
349
+ }
350
+
351
+ for (const row of rows) {
352
+ row[rel] = related.filter(r => r[foreignKey] === row.id);
353
+ }
354
+ } else if (type === 'belongsTo') {
355
+ const ids = [...new Set(rows.map(r => r[foreignKey]).filter(Boolean))];
356
+ if (ids.length === 0) {
357
+ rows.forEach(r => r[rel] = null);
358
+ continue;
359
+ }
360
+
361
+ let related;
362
+ if (nested.length > 0) {
363
+ related = await RelModel.with(nested).whereIn('id', ids).get();
364
+ } else {
365
+ related = await RelModel.query().whereIn('id', ids);
366
+ }
367
+
368
+ const map = Object.fromEntries(related.map(r => [r.id, r]));
369
+ for (const row of rows) {
370
+ row[rel] = map[row[foreignKey]] || null;
371
+ }
372
+ }
373
+ }
374
+ return rows;
375
+ }
376
+ }
377
+
378
+ module.exports = { ORM, Model, EagerBuilder };
379
+
@@ -0,0 +1,179 @@
1
+ /**
2
+ * database/query-builder.js
3
+ * Fluent Query Builder for Free Framework ORM.
4
+ * Chainable API: Model.query().where("likes",">",10).orderBy("created_at","desc").limit(20).get()
5
+ */
6
+
7
+ class QueryBuilder {
8
+ constructor(db, tableName) {
9
+ this._db = db;
10
+ this._table = tableName;
11
+ this._query = db(tableName);
12
+ this._eagerRelations = [];
13
+ this._ModelClass = null;
14
+ }
15
+
16
+ // ── Conditions ───────────────────────────────────────────────────────────
17
+
18
+ /**
19
+ * Add a WHERE clause.
20
+ * @param {string} column
21
+ * @param {string} [op] - operator: "=", ">", "<", ">=", "<=", "!=", "like", "in"
22
+ * @param {*} value
23
+ *
24
+ * Usage:
25
+ * .where("name", "Omar") → WHERE name = 'Omar'
26
+ * .where("likes", ">", 10) → WHERE likes > 10
27
+ * .where("title", "like", "%hi%") → WHERE title LIKE '%hi%'
28
+ */
29
+ where(column, opOrValue, value) {
30
+ if (value === undefined) {
31
+ // Two-arg form: where("name", "Omar")
32
+ this._query = this._query.where(column, opOrValue);
33
+ } else {
34
+ const op = opOrValue.toLowerCase();
35
+ if (op === 'like') {
36
+ this._query = this._query.whereLike(column, value);
37
+ } else if (op === 'in') {
38
+ this._query = this._query.whereIn(column, value);
39
+ } else if (op === 'not in') {
40
+ this._query = this._query.whereNotIn(column, value);
41
+ } else {
42
+ this._query = this._query.where(column, opOrValue, value);
43
+ }
44
+ }
45
+ return this;
46
+ }
47
+
48
+ whereNull(column) { this._query = this._query.whereNull(column); return this; }
49
+ whereNotNull(column) { this._query = this._query.whereNotNull(column); return this; }
50
+ whereIn(col, arr) { this._query = this._query.whereIn(col, arr); return this; }
51
+
52
+ orWhere(column, opOrValue, value) {
53
+ if (value === undefined) {
54
+ this._query = this._query.orWhere(column, opOrValue);
55
+ } else {
56
+ this._query = this._query.orWhere(column, opOrValue, value);
57
+ }
58
+ return this;
59
+ }
60
+
61
+ // ── Ordering & Limiting ──────────────────────────────────────────────────
62
+
63
+ orderBy(column, direction = 'asc') {
64
+ this._query = this._query.orderBy(column, direction);
65
+ return this;
66
+ }
67
+
68
+ limit(n) { this._query = this._query.limit(n); return this; }
69
+ offset(n) { this._query = this._query.offset(n); return this; }
70
+
71
+ // ── Selection ────────────────────────────────────────────────────────────
72
+
73
+ select(...columns) {
74
+ this._query = this._query.select(...columns);
75
+ return this;
76
+ }
77
+
78
+ // ── Aggregates ───────────────────────────────────────────────────────────
79
+
80
+ async count(col = 'id') {
81
+ const [{ total }] = await this._db(this._table).count(`${col} as total`);
82
+ return Number(total);
83
+ }
84
+
85
+ async sum(col) {
86
+ const [{ s }] = await this._query.clone().sum(`${col} as s`);
87
+ return Number(s) || 0;
88
+ }
89
+
90
+ async avg(col) {
91
+ const [{ a }] = await this._query.clone().avg(`${col} as a`);
92
+ return Number(a) || 0;
93
+ }
94
+
95
+ async max(col) {
96
+ const [{ m }] = await this._query.clone().max(`${col} as m`);
97
+ return m;
98
+ }
99
+
100
+ async min(col) {
101
+ const [{ m }] = await this._query.clone().min(`${col} as m`);
102
+ return m;
103
+ }
104
+
105
+ // ── Eager Loading ─────────────────────────────────────────────────────────
106
+
107
+ with(relations) {
108
+ const arr = Array.isArray(relations) ? relations : [relations];
109
+ this._eagerRelations = this._eagerRelations.concat(arr);
110
+ return this;
111
+ }
112
+
113
+ // ── Execution ─────────────────────────────────────────────────────────────
114
+
115
+ async get() {
116
+ const rows = await this._query;
117
+ if (this._eagerRelations.length > 0 && this._ModelClass) {
118
+ return this._loadEager(rows);
119
+ }
120
+ return rows;
121
+ }
122
+
123
+ async first() {
124
+ this._query = this._query.first();
125
+ return this._query;
126
+ }
127
+
128
+ async pluck(column) {
129
+ const rows = await this._query.select(column);
130
+ return rows.map(r => r[column]);
131
+ }
132
+
133
+ async paginate(page = 1, perPage = 20) {
134
+ const total = await this.count();
135
+ const offset = (page - 1) * perPage;
136
+ const data = await this._db(this._table).limit(perPage).offset(offset);
137
+ return {
138
+ data,
139
+ total,
140
+ page,
141
+ perPage,
142
+ lastPage: Math.ceil(total / perPage),
143
+ hasNext: page < Math.ceil(total / perPage),
144
+ };
145
+ }
146
+
147
+ then(resolve, reject) {
148
+ return this._query.then(resolve, reject);
149
+ }
150
+
151
+ async _loadEager(rows) {
152
+ if (!this._ModelClass || !this._ModelClass.relations) return rows;
153
+ const declared = this._ModelClass.relations;
154
+
155
+ for (const rel of this._eagerRelations) {
156
+ const def = declared[rel];
157
+ if (!def) continue;
158
+ const { model: RelModel, foreignKey, type } = def;
159
+
160
+ if (type === 'hasMany') {
161
+ const ids = rows.map(r => r.id);
162
+ const related = await this._db(RelModel._tableName()).whereIn(foreignKey, ids);
163
+ for (const row of rows) {
164
+ row[rel] = related.filter(r => r[foreignKey] === row.id);
165
+ }
166
+ } else if (type === 'belongsTo') {
167
+ const ids = [...new Set(rows.map(r => r[foreignKey]).filter(Boolean))];
168
+ const related = await this._db(RelModel._tableName()).whereIn('id', ids);
169
+ const map = Object.fromEntries(related.map(r => [r.id, r]));
170
+ for (const row of rows) {
171
+ row[rel] = map[row[foreignKey]] || null;
172
+ }
173
+ }
174
+ }
175
+ return rows;
176
+ }
177
+ }
178
+
179
+ module.exports = { QueryBuilder };
package/index.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * index.js
3
+ * Public API entry point for the Free Framework NPM package.
4
+ * Exports all primary runtime, compiler, database, and plugin modules.
5
+ */
6
+
7
+ // Core Runtime
8
+ const { FreeServer } = require('./runtime/server');
9
+ const { ClusterManager } = require('./runtime/cluster');
10
+ const { EdgeRuntime } = require('./runtime/edge');
11
+
12
+ // Compiler
13
+ const { tokenize } = require('./compiler/lexer');
14
+ const { parse } = require('./compiler/parser');
15
+ const { generate } = require('./compiler/generator');
16
+
17
+ // Database
18
+ const { Model } = require('./database/model');
19
+ const { ORM } = require('./database/orm');
20
+
21
+ // Built-in Plugins
22
+ const AuthPlugin = require('./plugins/auth');
23
+ const UploadPlugin = require('./plugins/upload');
24
+ const ChatPlugin = require('./plugins/chat');
25
+
26
+ // Utils
27
+ const logger = require('./utils/logger');
28
+
29
+ module.exports = {
30
+ // Core
31
+ FreeServer,
32
+ ClusterManager,
33
+ EdgeRuntime,
34
+
35
+ // Compiler
36
+ tokenize,
37
+ parse,
38
+ generate,
39
+
40
+ // Database
41
+ Model,
42
+ ORM,
43
+
44
+ // Plugins
45
+ AuthPlugin,
46
+ UploadPlugin,
47
+ ChatPlugin,
48
+
49
+ // Utils
50
+ logger,
51
+ };
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "free-framework",
3
+ "version": "4.4.0",
4
+ "description": "Professional Node.js engine for the .free language. Blazing-fast SSR, Islands Architecture, and built-in ORM.",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "free": "bin/free.js"
8
+ },
9
+ "scripts": {
10
+ "test": "vitest run",
11
+ "dev": "node bin/free.js serve",
12
+ "build": "node bin/free.js build",
13
+ "doctor": "node bin/free.js doctor"
14
+ },
15
+ "keywords": [
16
+ "framework",
17
+ "web",
18
+ "full-stack",
19
+ "free",
20
+ "freejs",
21
+ "free-framework",
22
+ "hyper-express",
23
+ "compiler",
24
+ "ssr",
25
+ "islands-architecture"
26
+ ],
27
+ "author": "Dev Omar Tolba",
28
+ "homepage": "https://github.com/dev-omartolba",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/dev-omartolba/free-framework.git"
32
+ },
33
+ "license": "MIT",
34
+ "type": "commonjs",
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "files": [
39
+ "bin/",
40
+ "cli/",
41
+ "compiler/",
42
+ "database/",
43
+ "plugins/",
44
+ "router/",
45
+ "runtime/",
46
+ "template-engine/",
47
+ "templates/",
48
+ "utils/",
49
+ "index.js",
50
+ "LICENSE",
51
+ "README.md"
52
+ ],
53
+ "dependencies": {
54
+ "bcrypt": "^6.0.0",
55
+ "chalk": "^5.6.2",
56
+ "chokidar": "^5.0.0",
57
+ "clean-css": "^5.3.3",
58
+ "commander": "^14.0.3",
59
+ "cookie": "^1.1.1",
60
+ "dotenv": "^17.3.1",
61
+ "esbuild": "^0.27.3",
62
+ "express": "^5.2.1",
63
+ "fast-json-stringify": "^6.3.0",
64
+ "fastify": "^5.8.2",
65
+ "fs-extra": "^11.3.4",
66
+ "html-minifier-terser": "^7.2.0",
67
+ "hyper-express": "^6.17.3",
68
+ "jsonwebtoken": "^9.0.3",
69
+ "knex": "^3.1.0",
70
+ "live-directory": "^3.0.3",
71
+ "lru-cache": "^11.2.6",
72
+ "pino": "^10.3.1",
73
+ "pino-pretty": "^13.1.3",
74
+ "sqlite3": "^5.1.7",
75
+ "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.55.0"
76
+ },
77
+ "devDependencies": {
78
+ "vitest": "^3.0.0"
79
+ }
80
+ }