vibe-gx 4.1.1 → 4.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-gx",
3
- "version": "4.1.1",
3
+ "version": "4.1.2",
4
4
  "description": "A lightweight, high-performance Node.js web framework.",
5
5
  "type": "module",
6
6
  "main": "vibe.js",
@@ -1,4 +1,5 @@
1
1
  import os from "os";
2
+ import fs from "fs";
2
3
  import { color } from "../helpers/colors.js";
3
4
 
4
5
  const LOG_LEVELS = {
@@ -25,11 +26,20 @@ const LEVEL_NAMES = {
25
26
  export class Logger {
26
27
  constructor(options = {}) {
27
28
  this.level = LOG_LEVELS[options.level || "info"] || 30;
28
- this.prettyPrint = options.prettyPrint || false;
29
+ this.colors = options.colors !== undefined ? options.colors : true;
30
+ this.prettyPrint =
31
+ options.prettyPrint !== undefined ? options.prettyPrint : this.colors;
29
32
  this.lifecycle = options.lifecycle || false;
30
33
  this.stream = options.stream || process.stdout;
34
+ this.dest = options.dest || "console"; // "console", "file", "both"
35
+ this.logFile = options.logFile;
31
36
  this.bindings = options.bindings || {};
32
37
 
38
+ // Initialize file stream if needed
39
+ if (this.logFile && (this.dest === "file" || this.dest === "both")) {
40
+ this.fileStream = fs.createWriteStream(this.logFile, { flags: "a" });
41
+ }
42
+
33
43
  if (!this.bindings.pid) this.bindings.pid = process.pid;
34
44
  if (!this.bindings.hostname) this.bindings.hostname = os.hostname();
35
45
  }
@@ -42,9 +52,12 @@ export class Logger {
42
52
  level: Object.keys(LOG_LEVELS).find(
43
53
  (key) => LOG_LEVELS[key] === this.level,
44
54
  ),
55
+ colors: this.colors,
45
56
  prettyPrint: this.prettyPrint,
46
57
  lifecycle: this.lifecycle,
47
58
  stream: this.stream,
59
+ dest: this.dest,
60
+ logFile: this.logFile,
48
61
  bindings: { ...this.bindings, ...bindings },
49
62
  });
50
63
  }
@@ -107,10 +120,16 @@ export class Logger {
107
120
 
108
121
  const finalLog = { ...base, ...logData };
109
122
 
110
- if (this.prettyPrint) {
111
- this._printPretty(finalLog);
112
- } else {
113
- this.stream.write(JSON.stringify(finalLog) + "\n");
123
+ if (this.dest === "console" || this.dest === "both") {
124
+ if (this.prettyPrint) {
125
+ this._printPretty(finalLog);
126
+ } else {
127
+ this.stream.write(JSON.stringify(finalLog) + "\n");
128
+ }
129
+ }
130
+
131
+ if ((this.dest === "file" || this.dest === "both") && this.fileStream) {
132
+ this.fileStream.write(JSON.stringify(finalLog) + "\n");
114
133
  }
115
134
  }
116
135
 
@@ -95,7 +95,15 @@ async function server(options, port, host, callback) {
95
95
 
96
96
  if (options.loggerConfig && options.loggerConfig.lifecycle) {
97
97
  req.startTime = Date.now();
98
- req.log.info({ type: "req" }, "Incoming request");
98
+
99
+ // Determine sender IP early for logging
100
+ const sender =
101
+ req.socket.remoteAddress || req.headers["x-forwarded-for"] || "unknown";
102
+
103
+ req.log.info(
104
+ { type: "req", url: req.url, method: req.method, sender },
105
+ "Incoming request",
106
+ );
99
107
 
100
108
  res.on("finish", () => {
101
109
  req.log.info(
package/vibe.d.ts CHANGED
@@ -181,6 +181,12 @@ export interface LoggerConfig {
181
181
  lifecycle?: boolean;
182
182
  /** If true, formats JSON output into human-readable Vibe-styled terminal lines (like pino-pretty) */
183
183
  prettyPrint?: boolean;
184
+ /** If true, applies ANSI color formatting to terminal logs. Default: true (unless overridden) */
185
+ colors?: boolean;
186
+ /** Destination for the logs. Accepts "console", "file", or "both". Default: "console" */
187
+ dest?: "console" | "file" | "both";
188
+ /** Absolute or relative path to the target log file. Active when dest is "file" or "both" */
189
+ logFile?: string;
184
190
  /** Custom writable stream to output logs to (defaults to process.stdout) */
185
191
  stream?: NodeJS.WritableStream;
186
192
  }
@@ -213,6 +219,8 @@ export interface LoggerAPI {
213
219
  export interface VibeConfig {
214
220
  /** Configuration for the native Vibe terminal logger */
215
221
  logger?: LoggerConfig | boolean;
222
+ /** Enable automatic process restarting on crash. Spawns an internal cluster manager. Default: false */
223
+ autoRestart?: boolean;
216
224
  }
217
225
 
218
226
  // ==========================================
package/vibe.js CHANGED
@@ -6,6 +6,7 @@ import { PathToRegex } from "./utils/core/handler.js";
6
6
  import { compileSerializer } from "./utils/core/compile-serializer.js";
7
7
  import { createLogger, Logger } from "./utils/core/logger.js";
8
8
  import { handleError } from "./utils/core/handler.js";
9
+ import { clusterize } from "./utils/scaling/cluster.js";
9
10
 
10
11
  /**
11
12
  * Helper to generate regex for a path
@@ -133,6 +134,7 @@ function pathToRegex(path) {
133
134
  * Initializes a Vibe application instance.
134
135
  * @param {Object} [config={}]
135
136
  * @param {Object|boolean} [config.logger] - Logger configuration
137
+ * @param {boolean} [config.autoRestart] - Restart server automatically on crash
136
138
  * @returns {VibeApp}
137
139
  */
138
140
  const vibe = (config = {}) => {
@@ -173,6 +175,22 @@ const vibe = (config = {}) => {
173
175
  errorHandler: handleError,
174
176
  };
175
177
 
178
+ // Add global uncaught exception handler to prevent silent deaths and log cleanly
179
+ process.on("uncaughtException", (err) => {
180
+ appLogger.fatal(err, "Uncaught Exception crashed the server");
181
+ // Only exit here if we're not exiting gracefully anyway,
182
+ // cluster manager will restart it.
183
+ setTimeout(() => process.exit(1), 100);
184
+ });
185
+
186
+ process.on("unhandledRejection", (reason, promise) => {
187
+ appLogger.fatal(
188
+ { err: reason },
189
+ "Unhandled Promise Rejection crashed the server",
190
+ );
191
+ setTimeout(() => process.exit(1), 100);
192
+ });
193
+
176
194
  // Register default landing route
177
195
  const defaultRoute = {
178
196
  method: "GET",
@@ -377,7 +395,13 @@ const vibe = (config = {}) => {
377
395
  host = undefined;
378
396
  }
379
397
 
380
- server(options, Number(port), host, callback);
398
+ const startServer = () => server(options, Number(port), host, callback);
399
+
400
+ if (config.autoRestart) {
401
+ clusterize(startServer, { workers: 1, restart: true });
402
+ } else {
403
+ startServer();
404
+ }
381
405
  }
382
406
 
383
407
  /**