@nitronjs/framework 0.2.3 → 0.2.5
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 +14 -7
- 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 +0 -1
- package/lib/Console/Commands/MigrateFreshCommand.js +17 -25
- package/lib/Console/Commands/MigrateRollbackCommand.js +6 -3
- package/lib/Console/Commands/MigrateStatusCommand.js +6 -3
- package/lib/Console/Commands/SeedCommand.js +4 -2
- package/lib/Console/Commands/StorageLinkCommand.js +20 -5
- package/lib/Console/Output.js +142 -0
- package/lib/Core/Config.js +2 -1
- package/lib/Core/Paths.js +8 -0
- 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 +56 -61
- package/lib/Database/Model.js +157 -83
- package/lib/Database/QueryBuilder.js +31 -0
- package/lib/Database/QueryValidation.js +36 -44
- package/lib/Database/Schema/Blueprint.js +25 -36
- package/lib/Database/Schema/Manager.js +31 -68
- package/lib/Database/Seeder/SeederRunner.js +12 -31
- 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 +81 -10
- package/lib/Hashing/Hash.js +41 -0
- package/lib/Http/Server.js +177 -152
- 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 +94 -51
- package/lib/View/Client/nitronjs-icon.png +0 -0
- package/lib/View/{Manager.js → View.js} +44 -29
- package/lib/index.d.ts +42 -8
- package/lib/index.js +19 -12
- package/package.json +1 -1
- package/skeleton/app/Controllers/HomeController.js +7 -1
- 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/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
package/lib/Http/Server.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { pathToFileURL } from "node:url";
|
|
2
1
|
import dotenv from "dotenv";
|
|
3
|
-
import path from "node:path";
|
|
4
2
|
import fastify from "fastify";
|
|
5
3
|
import fastifyCors from "@fastify/cors";
|
|
6
4
|
import fastifyStatic from "@fastify/static";
|
|
@@ -8,69 +6,103 @@ import fastifyCookie from "@fastify/cookie";
|
|
|
8
6
|
import fastifyHelmet from "@fastify/helmet";
|
|
9
7
|
import fastifyMultipart from "@fastify/multipart";
|
|
10
8
|
import fastifyFormbody from "@fastify/formbody";
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import path from "node:path";
|
|
11
12
|
import Paths from "../Core/Paths.js";
|
|
12
13
|
import Config from "../Core/Config.js";
|
|
13
14
|
import Environment from "../Core/Environment.js";
|
|
14
|
-
import
|
|
15
|
-
import View from "../View/
|
|
16
|
-
import Auth from "../Auth/
|
|
15
|
+
import Router from "../Route/Router.js";
|
|
16
|
+
import View from "../View/View.js";
|
|
17
|
+
import Auth from "../Auth/Auth.js";
|
|
17
18
|
import SessionManager from "../Session/Manager.js";
|
|
18
19
|
import DB from "../Database/DB.js";
|
|
19
|
-
import Log from "../Logging/
|
|
20
|
-
import Loader from "../Route/Loader.js";
|
|
20
|
+
import Log from "../Logging/Log.js";
|
|
21
21
|
import HMRServer from "../HMR/Server.js";
|
|
22
22
|
|
|
23
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), "utf8"));
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* HTTP server manager built on Fastify.
|
|
28
|
+
* Handles server configuration, plugin registration, and lifecycle management.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* import { Server } from "@nitronjs/framework";
|
|
32
|
+
* await Server.start();
|
|
33
|
+
*/
|
|
23
34
|
class Server {
|
|
24
35
|
static #server;
|
|
25
|
-
static #
|
|
36
|
+
static #config;
|
|
26
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Starts the HTTP server with all configured plugins and routes.
|
|
40
|
+
* @returns {Promise<void>}
|
|
41
|
+
*/
|
|
27
42
|
static async start() {
|
|
28
43
|
dotenv.config({ quiet: true });
|
|
29
|
-
|
|
30
|
-
// Initialize config system first (loads all configs once)
|
|
31
44
|
await Config.initialize();
|
|
32
|
-
|
|
33
|
-
// Load project configs dynamically
|
|
34
|
-
this.#serverConfigs = Config.all("server");
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
this.#config = Config.all("server");
|
|
47
|
+
const { web_server = {}, cors = {} } = this.#config;
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
await Route.setup(this.#server);
|
|
46
|
-
View.setup(this.#server);
|
|
47
|
-
await this.#listen();
|
|
48
|
-
}
|
|
49
|
+
const serverDefaults = {
|
|
50
|
+
bodyLimit: 50 * 1024 * 1024,
|
|
51
|
+
trustProxy: true,
|
|
52
|
+
maxParamLength: 150
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const webServerConfig = { ...serverDefaults, ...web_server };
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
// Create Fastify instance
|
|
51
58
|
this.#server = fastify({
|
|
52
59
|
logger: false,
|
|
53
|
-
bodyLimit:
|
|
54
|
-
trustProxy:
|
|
60
|
+
bodyLimit: webServerConfig.bodyLimit,
|
|
61
|
+
trustProxy: webServerConfig.trustProxy,
|
|
55
62
|
requestIdHeader: "x-request-id",
|
|
56
63
|
exposeHeadRoutes: false,
|
|
57
64
|
requestTimeout: 60000,
|
|
58
65
|
connectionTimeout: 120000,
|
|
59
66
|
routerOptions: {
|
|
60
67
|
ignoreTrailingSlash: true,
|
|
61
|
-
maxParamLength:
|
|
68
|
+
maxParamLength: webServerConfig.maxParamLength
|
|
62
69
|
}
|
|
63
70
|
});
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
this.#registerHooks();
|
|
67
|
-
this.#registerShutdownHandlers();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
static #registerPlugins() {
|
|
72
|
+
// Register plugins
|
|
71
73
|
this.#server.register(fastifyCors, {
|
|
72
|
-
origin:
|
|
73
|
-
|
|
74
|
+
origin: (requestOrigin, cb) => {
|
|
75
|
+
if (!requestOrigin) {
|
|
76
|
+
return cb(null, true);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const origin = cors.origin;
|
|
80
|
+
|
|
81
|
+
if (origin === "*") {
|
|
82
|
+
return cb(null, true);
|
|
83
|
+
}
|
|
84
|
+
else if (origin === "auto") {
|
|
85
|
+
const url = process.env.APP_URL;
|
|
86
|
+
const port = process.env.APP_PORT;
|
|
87
|
+
|
|
88
|
+
if (!url) {
|
|
89
|
+
return cb(null, false);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const appOrigin = !port || port === "80" || port === "443" ? url : `${url}:${port}`;
|
|
93
|
+
|
|
94
|
+
return cb(null, requestOrigin === appOrigin);
|
|
95
|
+
}
|
|
96
|
+
else if (typeof origin === "string") {
|
|
97
|
+
return cb(null, requestOrigin === origin);
|
|
98
|
+
}
|
|
99
|
+
else if (Array.isArray(origin)) {
|
|
100
|
+
return cb(null, origin.includes(requestOrigin));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return cb(null, false);
|
|
104
|
+
},
|
|
105
|
+
credentials: cors.origin === "*" ? false : cors.credentials === true
|
|
74
106
|
});
|
|
75
107
|
|
|
76
108
|
this.#server.register(fastifyStatic, {
|
|
@@ -101,110 +133,90 @@ class Server {
|
|
|
101
133
|
xssFilter: true
|
|
102
134
|
});
|
|
103
135
|
|
|
136
|
+
const multipartDefaults = {
|
|
137
|
+
fieldNameSize: 100,
|
|
138
|
+
fieldSize: 2 * 1024 * 1024,
|
|
139
|
+
maxFields: 50,
|
|
140
|
+
maxFileSize: 20 * 1024 * 1024,
|
|
141
|
+
maxFiles: 10,
|
|
142
|
+
maxParts: 60,
|
|
143
|
+
attachFieldsToBody: true
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const multipartConfig = { ...multipartDefaults, ...web_server.multipart };
|
|
147
|
+
|
|
104
148
|
this.#server.register(fastifyMultipart, {
|
|
105
149
|
limits: {
|
|
106
|
-
fieldNameSize:
|
|
107
|
-
fieldSize:
|
|
108
|
-
fields:
|
|
109
|
-
fileSize:
|
|
110
|
-
files:
|
|
111
|
-
parts:
|
|
150
|
+
fieldNameSize: multipartConfig.fieldNameSize,
|
|
151
|
+
fieldSize: multipartConfig.fieldSize,
|
|
152
|
+
fields: multipartConfig.maxFields,
|
|
153
|
+
fileSize: multipartConfig.maxFileSize,
|
|
154
|
+
files: multipartConfig.maxFiles,
|
|
155
|
+
parts: multipartConfig.maxParts
|
|
112
156
|
},
|
|
113
|
-
attachFieldsToBody:
|
|
157
|
+
attachFieldsToBody: multipartConfig.attachFieldsToBody
|
|
114
158
|
});
|
|
115
159
|
|
|
116
160
|
this.#server.register(fastifyFormbody);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
static #getCorsOrigin(requestOrigin, cb) {
|
|
120
|
-
if (!requestOrigin) return cb(null, true);
|
|
121
|
-
|
|
122
|
-
const { origin } = this.#serverConfigs.cors;
|
|
123
|
-
|
|
124
|
-
if (origin === "*") return cb(null, true);
|
|
125
|
-
|
|
126
|
-
if (origin === "auto") {
|
|
127
|
-
const url = process.env.APP_URL;
|
|
128
|
-
const port = process.env.APP_PORT;
|
|
129
|
-
if (!url) return cb(null, false);
|
|
130
161
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (Array.isArray(origin)) return cb(null, origin.includes(requestOrigin));
|
|
162
|
+
// Register hooks
|
|
163
|
+
this.#server.addHook("preHandler", async (req) => {
|
|
164
|
+
if (!req.isMultipart() || !req.body) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
137
167
|
|
|
138
|
-
|
|
139
|
-
|
|
168
|
+
const normalized = {};
|
|
169
|
+
const files = {};
|
|
140
170
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
171
|
+
for (const [key, field] of Object.entries(req.body)) {
|
|
172
|
+
if (field?.toBuffer) {
|
|
173
|
+
if (field.filename) {
|
|
174
|
+
files[key] = field;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else if (field && typeof field === "object" && "value" in field) {
|
|
178
|
+
normalized[key] = field.value;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
normalized[key] = field;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
145
184
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return data;
|
|
149
|
-
}
|
|
185
|
+
const data = { ...normalized, ...files };
|
|
186
|
+
const hasBrackets = Object.keys(data).some(key => key.includes("["));
|
|
150
187
|
|
|
151
|
-
|
|
188
|
+
if (!hasBrackets) {
|
|
189
|
+
req.body = data;
|
|
152
190
|
|
|
153
|
-
|
|
154
|
-
if (!key.includes("[")) {
|
|
155
|
-
result[key] = value;
|
|
156
|
-
continue;
|
|
191
|
+
return;
|
|
157
192
|
}
|
|
158
193
|
|
|
159
|
-
const
|
|
160
|
-
let current = result;
|
|
161
|
-
|
|
162
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
163
|
-
const part = parts[i];
|
|
164
|
-
const nextPart = parts[i + 1];
|
|
165
|
-
const isNextIndex = /^\d+$/.test(nextPart);
|
|
194
|
+
const result = {};
|
|
166
195
|
|
|
167
|
-
|
|
168
|
-
|
|
196
|
+
for (const [key, value] of Object.entries(data)) {
|
|
197
|
+
if (!key.includes("[")) {
|
|
198
|
+
result[key] = value;
|
|
199
|
+
continue;
|
|
169
200
|
}
|
|
170
201
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const lastPart = parts[parts.length - 1];
|
|
175
|
-
current[lastPart] = value;
|
|
176
|
-
}
|
|
202
|
+
const parts = key.replace(/\]/g, "").split("[");
|
|
203
|
+
let current = result;
|
|
177
204
|
|
|
178
|
-
|
|
179
|
-
|
|
205
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
206
|
+
const part = parts[i];
|
|
207
|
+
const isNextIndex = /^\d+$/.test(parts[i + 1]);
|
|
180
208
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (req.isMultipart() && req.body) {
|
|
184
|
-
const normalized = {};
|
|
185
|
-
const files = {};
|
|
186
|
-
|
|
187
|
-
for (const [key, field] of Object.entries(req.body)) {
|
|
188
|
-
if (field && typeof field === 'object') {
|
|
189
|
-
if (field.toBuffer) {
|
|
190
|
-
if (!field.filename) continue;
|
|
191
|
-
|
|
192
|
-
files[key] = field;
|
|
193
|
-
}
|
|
194
|
-
else if ('value' in field) {
|
|
195
|
-
normalized[key] = field.value;
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
normalized[key] = field;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
normalized[key] = field;
|
|
209
|
+
if (current[part] === undefined) {
|
|
210
|
+
current[part] = isNextIndex ? [] : {};
|
|
203
211
|
}
|
|
212
|
+
|
|
213
|
+
current = current[part];
|
|
204
214
|
}
|
|
205
215
|
|
|
206
|
-
|
|
216
|
+
current[parts[parts.length - 1]] = value;
|
|
207
217
|
}
|
|
218
|
+
|
|
219
|
+
req.body = result;
|
|
208
220
|
});
|
|
209
221
|
|
|
210
222
|
this.#server.addHook("onResponse", async (req, res) => {
|
|
@@ -220,26 +232,51 @@ class Server {
|
|
|
220
232
|
requestId: req.id
|
|
221
233
|
});
|
|
222
234
|
});
|
|
223
|
-
}
|
|
224
235
|
|
|
225
|
-
|
|
236
|
+
// Register shutdown handlers
|
|
226
237
|
const shutdown = async () => {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
console.log();
|
|
239
|
+
console.log("\x1b[33m⏻ Server shutting down...\x1b[0m");
|
|
240
|
+
console.log("\x1b[32m✓ Server stopped gracefully\x1b[0m");
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
(await SessionManager.getInstance()).stopGC();
|
|
244
|
+
await Promise.race([
|
|
245
|
+
DB.close(),
|
|
246
|
+
new Promise(resolve => setTimeout(resolve, 1000))
|
|
247
|
+
]);
|
|
248
|
+
await Promise.race([
|
|
249
|
+
this.#server.close(),
|
|
250
|
+
new Promise(resolve => setTimeout(resolve, 1000))
|
|
251
|
+
]);
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Ignore errors during shutdown
|
|
255
|
+
}
|
|
232
256
|
};
|
|
233
257
|
|
|
234
258
|
process.on("SIGTERM", shutdown);
|
|
235
259
|
process.on("SIGINT", shutdown);
|
|
236
|
-
}
|
|
237
260
|
|
|
238
|
-
|
|
261
|
+
// Setup managers
|
|
262
|
+
await DB.setup();
|
|
263
|
+
await SessionManager.setup(this.#server);
|
|
264
|
+
Auth.setup(this.#server);
|
|
265
|
+
|
|
266
|
+
// Register HMR routes before Router (dev only)
|
|
267
|
+
if (Environment.isDev) {
|
|
268
|
+
HMRServer.registerRoutes(this.#server);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
await Router.setup(this.#server);
|
|
272
|
+
View.setup(this.#server);
|
|
273
|
+
|
|
274
|
+
// Listen
|
|
239
275
|
try {
|
|
240
276
|
const url = process.env.APP_URL ? new URL(process.env.APP_URL) : null;
|
|
241
|
-
const host = url
|
|
277
|
+
const host = url?.hostname || "127.0.0.1";
|
|
242
278
|
const port = Number(process.env.APP_PORT) || 3000;
|
|
279
|
+
|
|
243
280
|
const address = await this.#server.listen({ host, port });
|
|
244
281
|
|
|
245
282
|
if (Environment.isDev) {
|
|
@@ -247,36 +284,30 @@ class Server {
|
|
|
247
284
|
}
|
|
248
285
|
|
|
249
286
|
this.#printBanner({ success: true, address, host, port });
|
|
250
|
-
|
|
251
|
-
Log.info("Server started successfully!", {
|
|
252
|
-
address,
|
|
253
|
-
host,
|
|
254
|
-
port,
|
|
255
|
-
environment: Environment.isDev ? "development" : "production"
|
|
256
|
-
});
|
|
287
|
+
Log.info("Server started successfully!", { address, host, port, environment: Environment.isDev ? "development" : "production" });
|
|
257
288
|
}
|
|
258
289
|
catch (err) {
|
|
259
290
|
this.#printBanner({ success: false, error: err });
|
|
260
|
-
|
|
261
|
-
Log.fatal("Server startup failed", {
|
|
262
|
-
error: err.message,
|
|
263
|
-
code: err.code,
|
|
264
|
-
stack: err.stack
|
|
265
|
-
});
|
|
266
|
-
|
|
291
|
+
Log.fatal("Server startup failed", { error: err.message, code: err.code, stack: err.stack });
|
|
267
292
|
process.exit(1);
|
|
268
293
|
}
|
|
269
294
|
}
|
|
270
295
|
|
|
296
|
+
// Private Methods
|
|
271
297
|
static #printBanner({ success, address, host, port, error }) {
|
|
272
|
-
|
|
273
|
-
|
|
298
|
+
if (this.#config.log.channel !== "none" && this.#config.log.channel !== "file") {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
274
301
|
|
|
275
302
|
const color = success ? "\x1b[32m" : "\x1b[31m";
|
|
276
303
|
const reset = "\x1b[0m";
|
|
277
304
|
const bold = "\x1b[1m";
|
|
278
305
|
const boxWidth = 70;
|
|
306
|
+
const stripAnsi = str => str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
307
|
+
const pad = (content, length) => content + " ".repeat(Math.max(0, length - stripAnsi(content).length));
|
|
279
308
|
|
|
309
|
+
const version = `v${packageJson.version}`;
|
|
310
|
+
|
|
280
311
|
const logo = `
|
|
281
312
|
${color}███╗ ██╗██╗████████╗██████╗ ██████╗ ███╗ ██╗ ██╗███████╗
|
|
282
313
|
████╗ ██║██║╚══██╔══╝██╔══██╗██╔═══██╗████╗ ██║ ██║██╔════╝
|
|
@@ -286,12 +317,6 @@ ${color}███╗ ██╗██╗████████╗████
|
|
|
286
317
|
╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚════╝ ╚══════╝${reset}
|
|
287
318
|
`;
|
|
288
319
|
|
|
289
|
-
const stripAnsi = str => str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
290
|
-
const pad = (content, length) => {
|
|
291
|
-
const visible = stripAnsi(content);
|
|
292
|
-
return content + " ".repeat(Math.max(0, length - visible.length));
|
|
293
|
-
};
|
|
294
|
-
|
|
295
320
|
const lines = success
|
|
296
321
|
? [
|
|
297
322
|
`${color}${bold}✓${reset} ${bold}Server started successfully!${reset}`,
|
|
@@ -299,7 +324,8 @@ ${color}███╗ ██╗██╗████████╗████
|
|
|
299
324
|
`${bold}Address:${reset} ${address}`,
|
|
300
325
|
`${bold}Host:${reset} ${host}`,
|
|
301
326
|
`${bold}Port:${reset} ${port}`,
|
|
302
|
-
`${bold}Mode:${reset} ${Environment.isDev ? "development" : "production"}
|
|
327
|
+
`${bold}Mode:${reset} ${Environment.isDev ? "development" : "production"}`,
|
|
328
|
+
`${bold}Version:${reset} ${version}`
|
|
303
329
|
]
|
|
304
330
|
: [
|
|
305
331
|
`${color}${bold}✕${reset} ${bold}Server failed to start${reset}`,
|
|
@@ -317,7 +343,6 @@ ${color}███╗ ██╗██╗████████╗████
|
|
|
317
343
|
|
|
318
344
|
console.log(`${color}│${reset} ${" ".repeat(boxWidth - 4)} ${color}│${reset}`);
|
|
319
345
|
console.log(`${color}└${"─".repeat(boxWidth - 2)}┘${reset}`);
|
|
320
|
-
console.log();
|
|
321
346
|
}
|
|
322
347
|
}
|
|
323
348
|
|