@nitronjs/framework 0.2.2 → 0.2.4
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 +3 -1
- package/cli/create.js +88 -72
- package/cli/njs.js +17 -19
- package/lib/Auth/Auth.js +167 -0
- package/lib/Build/CssBuilder.js +9 -0
- package/lib/Build/FileAnalyzer.js +16 -0
- package/lib/Build/HydrationBuilder.js +17 -0
- package/lib/Build/Manager.js +15 -0
- package/lib/Build/colors.js +4 -0
- package/lib/Build/plugins.js +84 -20
- package/lib/Console/Commands/DevCommand.js +13 -9
- package/lib/Console/Commands/MakeCommand.js +24 -10
- package/lib/Console/Commands/MigrateCommand.js +4 -3
- package/lib/Console/Commands/MigrateFreshCommand.js +22 -27
- package/lib/Console/Commands/MigrateRollbackCommand.js +8 -4
- package/lib/Console/Commands/MigrateStatusCommand.js +8 -4
- package/lib/Console/Commands/SeedCommand.js +8 -28
- package/lib/Console/Commands/StorageLinkCommand.js +20 -5
- package/lib/Console/Output.js +143 -0
- package/lib/Core/Config.js +2 -1
- package/lib/Core/Paths.js +8 -8
- package/lib/Database/DB.js +141 -51
- package/lib/Database/Drivers/MySQLDriver.js +102 -157
- package/lib/Database/Migration/Checksum.js +3 -8
- package/lib/Database/Migration/MigrationRepository.js +25 -35
- package/lib/Database/Migration/MigrationRunner.js +59 -67
- package/lib/Database/Model.js +165 -75
- package/lib/Database/QueryBuilder.js +43 -0
- package/lib/Database/QueryValidation.js +51 -30
- package/lib/Database/Schema/Blueprint.js +25 -36
- package/lib/Database/Schema/Manager.js +31 -68
- package/lib/Database/Seeder/SeederRunner.js +24 -145
- package/lib/Date/DateTime.js +9 -0
- package/lib/Encryption/Encryption.js +52 -0
- package/lib/Faker/Faker.js +11 -0
- package/lib/Filesystem/Storage.js +120 -0
- package/lib/HMR/Server.js +79 -9
- package/lib/Hashing/Hash.js +41 -0
- package/lib/Http/Server.js +179 -151
- package/lib/Logging/{Manager.js → Log.js} +68 -80
- package/lib/Mail/Mail.js +187 -0
- package/lib/Route/Router.js +416 -0
- package/lib/Session/File.js +135 -233
- package/lib/Session/Manager.js +117 -171
- package/lib/Session/Memory.js +28 -38
- package/lib/Session/Session.js +71 -107
- package/lib/Support/Str.js +103 -0
- package/lib/Translation/Lang.js +54 -0
- package/lib/View/Client/hmr-client.js +87 -51
- package/lib/View/Client/nitronjs-icon.png +0 -0
- package/lib/View/{Manager.js → View.js} +44 -29
- package/lib/index.d.ts +49 -27
- package/lib/index.js +19 -13
- package/package.json +1 -1
- package/skeleton/app/Controllers/HomeController.js +7 -1
- package/skeleton/package.json +2 -0
- package/skeleton/resources/css/global.css +1 -0
- package/skeleton/resources/views/Site/Home.tsx +456 -79
- package/skeleton/tsconfig.json +6 -1
- package/lib/Auth/Manager.js +0 -111
- package/lib/Database/Connection.js +0 -61
- package/lib/Database/Manager.js +0 -162
- package/lib/Database/Migration/migrations/0000_00_00_00_01_create_seeders_table.js +0 -20
- package/lib/Database/Seeder/SeederRepository.js +0 -45
- package/lib/Encryption/Manager.js +0 -47
- package/lib/Filesystem/Manager.js +0 -74
- package/lib/Hashing/Manager.js +0 -25
- package/lib/Mail/Manager.js +0 -120
- package/lib/Route/Loader.js +0 -80
- package/lib/Route/Manager.js +0 -286
- package/lib/Translation/Manager.js +0 -49
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console output utilities for CLI commands.
|
|
3
|
+
* Provides consistent, professional styling for terminal messages.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const C = {
|
|
7
|
+
reset: "\x1b[0m",
|
|
8
|
+
bold: "\x1b[1m",
|
|
9
|
+
dim: "\x1b[2m",
|
|
10
|
+
red: "\x1b[31m",
|
|
11
|
+
green: "\x1b[32m",
|
|
12
|
+
yellow: "\x1b[33m",
|
|
13
|
+
blue: "\x1b[34m",
|
|
14
|
+
magenta: "\x1b[35m",
|
|
15
|
+
cyan: "\x1b[36m",
|
|
16
|
+
white: "\x1b[37m",
|
|
17
|
+
gray: "\x1b[90m"
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const I = {
|
|
21
|
+
success: "✔",
|
|
22
|
+
error: "✖",
|
|
23
|
+
warn: "⚠",
|
|
24
|
+
info: "◆",
|
|
25
|
+
pending: "◇",
|
|
26
|
+
arrow: "→",
|
|
27
|
+
bullet: "•",
|
|
28
|
+
sparkle: "✨",
|
|
29
|
+
database: "🗃️",
|
|
30
|
+
seed: "🌱"
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Common patterns
|
|
34
|
+
const LINE = `${C.gray}${"-".repeat(45)}${C.reset}`;
|
|
35
|
+
const DOTS = `${C.gray}···${C.reset}`;
|
|
36
|
+
const log = console.log.bind(console);
|
|
37
|
+
|
|
38
|
+
// Helper: prints section header
|
|
39
|
+
const header = (icon, color, title, subtitle = "") => {
|
|
40
|
+
log(` ${color}${icon}${C.reset} ${C.bold}${title}${C.reset}${subtitle ? ` ${C.gray}${subtitle}${C.reset}` : ""}`);
|
|
41
|
+
log(` ${LINE}`);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Helper: prints section footer with success
|
|
45
|
+
const footer = (message) => {
|
|
46
|
+
log(` ${LINE}`);
|
|
47
|
+
log(` ${C.green}${I.sparkle}${C.reset} ${C.green}${C.bold}${message}${C.reset}`);
|
|
48
|
+
log();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Helper: prints step row
|
|
52
|
+
const step = (icon, color, action, target, suffix = "") => {
|
|
53
|
+
log(` ${color}${icon}${C.reset} ${action} ${DOTS} ${target}${suffix}`);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Basic messages
|
|
57
|
+
const success = (msg) => log(`${C.green}${I.success}${C.reset} ${msg}`);
|
|
58
|
+
const error = (msg) => log(`${C.red}${I.error}${C.reset} ${msg}`);
|
|
59
|
+
const warn = (msg) => log(`${C.yellow}${I.warn}${C.reset} ${msg}`);
|
|
60
|
+
const info = (msg) => log(`${C.blue}${I.info}${C.reset} ${msg}`);
|
|
61
|
+
const dim = (msg) => log(`${C.dim}${msg}${C.reset}`);
|
|
62
|
+
const newline = () => log();
|
|
63
|
+
const errorDetail = (msg) => log(` ${C.dim}${msg}${C.reset}`);
|
|
64
|
+
|
|
65
|
+
// Task steps
|
|
66
|
+
const done = (action, target) => step(I.success, C.green, action, `${C.white}${target}${C.reset}`);
|
|
67
|
+
const pending = (action, target) => step(I.pending, C.gray, `${C.dim}${action}${C.reset}`, `${C.gray}${target}${C.reset}`);
|
|
68
|
+
|
|
69
|
+
// Migration
|
|
70
|
+
const migrationHeader = (batch) => header(I.database, C.magenta, "Running Migrations", `(batch ${batch})`);
|
|
71
|
+
const migrationSuccess = () => footer("All migrations completed successfully");
|
|
72
|
+
|
|
73
|
+
const frameworkMigration = (action, file) => {
|
|
74
|
+
const tag = `${C.magenta}[framework]${C.reset}`;
|
|
75
|
+
if (action === 'pending') {
|
|
76
|
+
step(I.pending, C.gray, `${C.dim}Migrating${C.reset}`, `${tag} ${C.gray}${file}${C.reset}`);
|
|
77
|
+
} else {
|
|
78
|
+
step(I.success, C.green, "Migrated ", `${tag} ${C.white}${file}${C.reset}`);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Seeder
|
|
83
|
+
const seederHeader = () => header(I.seed, C.cyan, "Running Seeders");
|
|
84
|
+
const seederSuccess = () => footer("All seeders completed successfully");
|
|
85
|
+
|
|
86
|
+
// Drop tables
|
|
87
|
+
const dropTablesHeader = () => header(I.warn, C.yellow, "Dropping Tables");
|
|
88
|
+
const droppingTable = (name) => step(I.pending, C.yellow, `${C.dim}Dropping${C.reset}`, `${C.yellow}${name}${C.reset}`);
|
|
89
|
+
const dropTablesSuccess = (count) => {
|
|
90
|
+
log(` ${LINE}`);
|
|
91
|
+
log(` ${C.green}${I.success}${C.reset} ${C.dim}Dropped ${count} table(s)${C.reset}`);
|
|
92
|
+
log();
|
|
93
|
+
log();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Rollback
|
|
97
|
+
const rollbackHeader = (count) => header(I.arrow, C.yellow, "Rolling Back", `(${count} migration${count > 1 ? 's' : ''})`);
|
|
98
|
+
const rollbackDone = (file, batch) => step(I.success, C.yellow, "Rolled back", `${C.white}${file}${C.reset}`, ` ${C.gray}(batch ${batch})${C.reset}`);
|
|
99
|
+
const rollbackSuccess = () => footer("Rollback completed successfully");
|
|
100
|
+
|
|
101
|
+
// Status
|
|
102
|
+
const statusHeader = () => header(I.info, C.blue, "Migration Status");
|
|
103
|
+
const statusRow = (status, name, batch) => {
|
|
104
|
+
if (status === 'Ran') {
|
|
105
|
+
step(I.success, C.green, `${C.green}Ran${C.reset} `, `${C.white}${name}${C.reset}`, ` ${C.gray}(batch ${batch})${C.reset}`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
step(I.pending, C.yellow, `${C.yellow}Pending${C.reset}`, `${C.gray}${name}${C.reset}`);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const statusFooter = (total, ran, pend) => {
|
|
112
|
+
log(` ${LINE}`);
|
|
113
|
+
log(` ${C.dim}Total: ${total} | Ran: ${ran} | Pending: ${pend}${C.reset}`);
|
|
114
|
+
log();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default {
|
|
118
|
+
COLORS: C,
|
|
119
|
+
ICONS: I,
|
|
120
|
+
success,
|
|
121
|
+
error,
|
|
122
|
+
warn,
|
|
123
|
+
info,
|
|
124
|
+
dim,
|
|
125
|
+
newline,
|
|
126
|
+
errorDetail,
|
|
127
|
+
done,
|
|
128
|
+
pending,
|
|
129
|
+
migrationHeader,
|
|
130
|
+
migrationSuccess,
|
|
131
|
+
frameworkMigration,
|
|
132
|
+
seederHeader,
|
|
133
|
+
seederSuccess,
|
|
134
|
+
dropTablesHeader,
|
|
135
|
+
droppingTable,
|
|
136
|
+
dropTablesSuccess,
|
|
137
|
+
rollbackHeader,
|
|
138
|
+
rollbackDone,
|
|
139
|
+
rollbackSuccess,
|
|
140
|
+
statusHeader,
|
|
141
|
+
statusRow,
|
|
142
|
+
statusFooter
|
|
143
|
+
};
|
package/lib/Core/Config.js
CHANGED
package/lib/Core/Paths.js
CHANGED
|
@@ -4,6 +4,14 @@ import { pathToFileURL } from "url";
|
|
|
4
4
|
|
|
5
5
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Path manager providing consistent access to framework and project directories.
|
|
9
|
+
* All paths are resolved relative to the project root (process.cwd()).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* Paths.controllers // /project/app/Controllers
|
|
13
|
+
* Paths.views // /project/resources/views
|
|
14
|
+
*/
|
|
7
15
|
class Paths {
|
|
8
16
|
static #framework = path.resolve(__dirname, "../..");
|
|
9
17
|
static #project = process.cwd();
|
|
@@ -92,14 +100,6 @@ class Paths {
|
|
|
92
100
|
return path.join(this.#project, "database/seeders");
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
static get seedersProd() {
|
|
96
|
-
return path.join(this.#project, "database/seeders/prod");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
static get seedersDev() {
|
|
100
|
-
return path.join(this.#project, "database/seeders/dev");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
103
|
static get storage() {
|
|
104
104
|
return path.join(this.#project, "storage");
|
|
105
105
|
}
|
package/lib/Database/DB.js
CHANGED
|
@@ -1,84 +1,174 @@
|
|
|
1
|
-
import
|
|
1
|
+
import MySQLDriver from "./Drivers/MySQLDriver.js";
|
|
2
2
|
import { query as createQueryBuilder, RawExpression } from "./QueryBuilder.js";
|
|
3
|
+
import Config from "../Core/Config.js";
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Database manager providing query builder access, raw queries, and transactions.
|
|
7
|
+
* Supports multiple database drivers (currently MySQL).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const users = await DB.table("users").where("active", 1).get();
|
|
11
|
+
* await DB.transaction(async (conn) => { ... });
|
|
12
|
+
*/
|
|
4
13
|
class DB {
|
|
5
|
-
static
|
|
6
|
-
|
|
14
|
+
static #driver = null;
|
|
15
|
+
static #config = null;
|
|
16
|
+
static #credentials = null;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Initializes database connection with configured driver.
|
|
20
|
+
* @returns {Promise<void>}
|
|
21
|
+
* @throws {Error} If health check fails
|
|
22
|
+
*/
|
|
23
|
+
static async setup() {
|
|
24
|
+
this.#config = Config.all("database");
|
|
25
|
+
|
|
26
|
+
const envDriver = (process.env.DATABASE_DRIVER || "mysql").toLowerCase();
|
|
27
|
+
|
|
28
|
+
if (envDriver === "none") {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.#driver = this.#createDriver(envDriver);
|
|
33
|
+
|
|
34
|
+
const isHealthy = await this.#driver.healthCheck();
|
|
35
|
+
|
|
36
|
+
if (!isHealthy) {
|
|
37
|
+
throw new Error("Database connection health check failed. Check your .env credentials and ensure MySQL is running.");
|
|
38
|
+
}
|
|
7
39
|
}
|
|
8
40
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Closes database connection and releases resources.
|
|
43
|
+
* @returns {Promise<void>}
|
|
44
|
+
*/
|
|
45
|
+
static async close() {
|
|
46
|
+
if (this.#driver) {
|
|
47
|
+
await this.#driver.close();
|
|
48
|
+
this.#driver = null;
|
|
49
|
+
}
|
|
12
50
|
}
|
|
13
51
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Creates a query builder for the specified table.
|
|
54
|
+
* @param {string} table - Table name
|
|
55
|
+
* @param {Function|null} modelClass - Optional model class for result mapping
|
|
56
|
+
* @returns {Object} Query builder instance
|
|
57
|
+
* @throws {Error} If database is disabled
|
|
58
|
+
*/
|
|
59
|
+
static table(table, modelClass = null) {
|
|
60
|
+
if (!this.#driver) {
|
|
61
|
+
throw new Error("Database is disabled (DATABASE_DRIVER=none)");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return createQueryBuilder(table, this.#driver, modelClass);
|
|
17
65
|
}
|
|
18
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Creates a raw SQL expression for use in queries.
|
|
69
|
+
* @param {string} expression - Raw SQL expression
|
|
70
|
+
* @returns {RawExpression} Raw expression wrapper
|
|
71
|
+
*/
|
|
19
72
|
static rawExpr(expression) {
|
|
20
73
|
return new RawExpression(expression);
|
|
21
74
|
}
|
|
22
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Executes a raw SQL query.
|
|
78
|
+
* @param {string} sql - Raw SQL string
|
|
79
|
+
* @returns {Promise<Array>} Query result
|
|
80
|
+
* @throws {Error} If database is disabled
|
|
81
|
+
*/
|
|
23
82
|
static async rawQuery(sql) {
|
|
24
|
-
if (!this.#
|
|
25
|
-
|
|
26
|
-
|
|
83
|
+
if (!this.#driver) {
|
|
84
|
+
throw new Error("Database is disabled (DATABASE_DRIVER=none)");
|
|
85
|
+
}
|
|
27
86
|
|
|
28
|
-
|
|
29
|
-
if (!this.#isEnabled()) return null;
|
|
30
|
-
return await this.connection().withTransaction(callback);
|
|
87
|
+
return await this.#driver.raw(sql);
|
|
31
88
|
}
|
|
32
89
|
|
|
33
90
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* @param {
|
|
37
|
-
* @param {
|
|
38
|
-
* @
|
|
39
|
-
* @
|
|
91
|
+
* Executes a callback within a database transaction.
|
|
92
|
+
* @param {Function} callback - Async function receiving connection
|
|
93
|
+
* @param {Object} options - Transaction options
|
|
94
|
+
* @param {number} options.timeout - Timeout in milliseconds (default: 30000)
|
|
95
|
+
* @returns {Promise<*>} Callback result
|
|
96
|
+
* @throws {Error} If transaction times out or fails
|
|
40
97
|
*/
|
|
41
|
-
static
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
static async setup() {
|
|
48
|
-
if (!this.#isEnabled()) return;
|
|
98
|
+
static async transaction(callback, options = {}) {
|
|
99
|
+
if (!this.#driver) {
|
|
100
|
+
throw new Error("Database is disabled (DATABASE_DRIVER=none)");
|
|
101
|
+
}
|
|
49
102
|
|
|
50
|
-
const
|
|
51
|
-
const
|
|
103
|
+
const timeout = options.timeout || 30000;
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
52
106
|
|
|
53
107
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
108
|
+
const result = await this.#driver.withTransaction(callback, { signal: controller.signal });
|
|
109
|
+
clearTimeout(timeoutId);
|
|
56
110
|
|
|
57
|
-
|
|
58
|
-
throw new Error(`Database connection '${defaultConnection}' health check failed`);
|
|
59
|
-
}
|
|
111
|
+
return result;
|
|
60
112
|
}
|
|
61
|
-
catch (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
113
|
+
catch (err) {
|
|
114
|
+
clearTimeout(timeoutId);
|
|
115
|
+
|
|
116
|
+
if (controller.signal.aborted) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Transaction timeout after ${timeout}ms. ` +
|
|
119
|
+
"Consider: 1) Adding await to all async operations, 2) Increasing timeout, 3) Optimizing queries."
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
throw err;
|
|
65
124
|
}
|
|
66
125
|
}
|
|
67
126
|
|
|
68
|
-
|
|
69
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Reconfigures database with new credentials.
|
|
129
|
+
* Used by installer to test and save database settings.
|
|
130
|
+
* @param {Object} credentials - Database credentials
|
|
131
|
+
* @param {string} credentials.host - Database host
|
|
132
|
+
* @param {number} credentials.port - Database port
|
|
133
|
+
* @param {string} credentials.database - Database name
|
|
134
|
+
* @param {string} credentials.username - Database username
|
|
135
|
+
* @param {string} credentials.password - Database password
|
|
136
|
+
* @returns {Promise<void>}
|
|
137
|
+
*/
|
|
138
|
+
static async reconfigure(credentials) {
|
|
139
|
+
await this.close();
|
|
70
140
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
141
|
+
this.#credentials = {
|
|
142
|
+
host: credentials.host,
|
|
143
|
+
port: String(credentials.port),
|
|
144
|
+
database: credentials.database,
|
|
145
|
+
username: credentials.username,
|
|
146
|
+
password: credentials.password || ""
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
this.#driver = this.#createDriver("mysql");
|
|
77
150
|
}
|
|
78
151
|
|
|
79
|
-
|
|
80
|
-
|
|
152
|
+
/** @private */
|
|
153
|
+
static #createDriver(driverName) {
|
|
154
|
+
const connections = this.#config?.connections || {};
|
|
155
|
+
const config = connections[driverName];
|
|
156
|
+
|
|
157
|
+
if (!config) {
|
|
158
|
+
throw new Error(`Database connection '${driverName}' is not configured`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
switch (config.driver) {
|
|
162
|
+
case "mysql":
|
|
163
|
+
return new MySQLDriver(config, this.#credentials);
|
|
164
|
+
case "postgres":
|
|
165
|
+
throw new Error("Postgres driver not yet implemented");
|
|
166
|
+
case "mongodb":
|
|
167
|
+
throw new Error("MongoDB driver not yet implemented");
|
|
168
|
+
default:
|
|
169
|
+
throw new Error(`Unknown database driver: ${config.driver}`);
|
|
170
|
+
}
|
|
81
171
|
}
|
|
82
172
|
}
|
|
83
173
|
|
|
84
|
-
export default DB;
|
|
174
|
+
export default DB;
|