create-phoenixjs 0.1.3 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-phoenixjs",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Create a new PhoenixJS project - A TypeScript framework inspired by Laravel, powered by Bun",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,22 @@
1
+ import { Controller } from '@framework/controller/Controller';
2
+
3
+ export class HealthController extends Controller {
4
+ /**
5
+ * Health check endpoint
6
+ */
7
+ async check() {
8
+ return this.json({
9
+ status: 'healthy',
10
+ timestamp: new Date().toISOString(),
11
+ uptime: process.uptime(),
12
+ service: 'PhoenixJS',
13
+ });
14
+ }
15
+
16
+ /**
17
+ * Ping endpoint
18
+ */
19
+ async ping() {
20
+ return this.text('pong');
21
+ }
22
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * PhoenixJS - Echo Gateway (Sample)
3
+ *
4
+ * A simple WebSocket gateway that echoes messages back to the sender.
5
+ */
6
+
7
+ import type { ServerWebSocket } from 'bun';
8
+ import { WebSocketGateway } from '@framework/gateway/WebSocketGateway';
9
+ import type { WebSocketData } from '@framework/gateway/Gateway';
10
+
11
+ export class EchoGateway extends WebSocketGateway {
12
+ readonly name = 'echo';
13
+ readonly path = '/ws/echo';
14
+
15
+ /**
16
+ * Called when a connection is opened
17
+ */
18
+ onOpen(ws: ServerWebSocket<WebSocketData>): void {
19
+ super.onOpen(ws);
20
+ console.log(`[EchoGateway] Client connected: ${ws.data.connectionId}`);
21
+
22
+ // Send welcome message
23
+ this.sendJson(ws, {
24
+ type: 'connected',
25
+ connectionId: ws.data.connectionId,
26
+ message: 'Welcome to EchoGateway!',
27
+ });
28
+ }
29
+
30
+ /**
31
+ * Called when a message is received - echo it back
32
+ */
33
+ onMessage(ws: ServerWebSocket<WebSocketData>, message: string | Buffer): void {
34
+ const text = typeof message === 'string' ? message : message.toString();
35
+ console.log(`[EchoGateway] Received: ${text}`);
36
+
37
+ // Try to parse as JSON
38
+ try {
39
+ const data = JSON.parse(text);
40
+ this.sendJson(ws, {
41
+ type: 'echo',
42
+ original: data,
43
+ timestamp: Date.now(),
44
+ });
45
+ } catch {
46
+ // Not JSON, echo as plain text
47
+ this.send(ws, `Echo: ${text}`);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Called when a connection is closed
53
+ */
54
+ onClose(ws: ServerWebSocket<WebSocketData>, code: number, reason: string): void {
55
+ console.log(`[EchoGateway] Client disconnected: ${ws.data.connectionId} (${code}: ${reason})`);
56
+ super.onClose(ws, code, reason);
57
+ }
58
+ }
@@ -7,6 +7,7 @@
7
7
  import { Application } from '@framework/core/Application';
8
8
  import { Kernel } from '@framework/core/Kernel';
9
9
  import { registerRoutes } from '@/routes/api';
10
+ import { appConfig } from '@/config/app';
10
11
 
11
12
  /**
12
13
  * Create and configure the application
@@ -15,13 +16,7 @@ export function createApplication(): Application {
15
16
  const app = Application.create();
16
17
 
17
18
  // Configure application
18
- app.configure({
19
- name: 'PhoenixJS',
20
- env: (process.env.NODE_ENV as 'development' | 'production' | 'testing') ?? 'development',
21
- debug: process.env.DEBUG === 'true' || process.env.NODE_ENV !== 'production',
22
- port: parseInt(process.env.PORT ?? '4000', 10),
23
- host: process.env.HOST ?? 'localhost',
24
- });
19
+ app.configure(appConfig);
25
20
 
26
21
  return app;
27
22
  }
@@ -0,0 +1,12 @@
1
+ import type { AppConfig } from '@framework/core/Application';
2
+
3
+ export const appConfig: AppConfig = {
4
+ name: process.env.APP_NAME || 'PhoenixJS',
5
+ env: (process.env.NODE_ENV as 'development' | 'production' | 'testing') || 'development',
6
+ debug: process.env.DEBUG === 'true' || process.env.NODE_ENV !== 'production',
7
+ port: parseInt(process.env.PORT || '4000', 10),
8
+ host: process.env.HOST || 'localhost',
9
+ logging: {
10
+ enabled: process.env.LOGGING_ENABLED !== 'false',
11
+ },
12
+ };
@@ -10,6 +10,7 @@ import { PluginManager } from '@framework/plugin/PluginManager';
10
10
  import { Plugin } from '@framework/plugin/Plugin';
11
11
  import { GatewayManager } from '@framework/gateway/GatewayManager';
12
12
  import { SecurityManager } from '@framework/security/SecurityManager';
13
+ import { Logger } from '@framework/log/Logger';
13
14
  import type { Gateway } from '@framework/gateway/Gateway';
14
15
  import type { SecurityConfig } from '@/config/security';
15
16
 
@@ -22,6 +23,9 @@ export interface AppConfig {
22
23
  plugins?: Plugin[];
23
24
  gateways?: Gateway[];
24
25
  security?: Partial<SecurityConfig>;
26
+ logging?: {
27
+ enabled?: boolean;
28
+ };
25
29
  }
26
30
 
27
31
  const defaultConfig: AppConfig = {
@@ -30,6 +34,9 @@ const defaultConfig: AppConfig = {
30
34
  debug: true,
31
35
  port: 4000,
32
36
  host: 'localhost',
37
+ logging: {
38
+ enabled: true
39
+ }
33
40
  };
34
41
 
35
42
  export class Application extends Container {
@@ -40,6 +47,7 @@ export class Application extends Container {
40
47
  private pluginManager: PluginManager;
41
48
  private gatewayManager: GatewayManager;
42
49
  private securityManager: SecurityManager;
50
+ private logger: Logger;
43
51
 
44
52
  constructor(basePath: string = process.cwd()) {
45
53
  super();
@@ -48,6 +56,7 @@ export class Application extends Container {
48
56
  this.pluginManager = new PluginManager(this);
49
57
  this.gatewayManager = new GatewayManager(this);
50
58
  this.securityManager = new SecurityManager(this);
59
+ this.logger = new Logger('Application');
51
60
  this.registerBaseBindings();
52
61
  }
53
62
 
@@ -78,6 +87,7 @@ export class Application extends Container {
78
87
  this.instance('pluginManager', this.pluginManager);
79
88
  this.instance('gatewayManager', this.gatewayManager);
80
89
  this.instance('securityManager', this.securityManager);
90
+ this.instance('logger', this.logger);
81
91
  }
82
92
 
83
93
  /**
@@ -87,6 +97,11 @@ export class Application extends Container {
87
97
  this.config = { ...this.config, ...config };
88
98
  this.instance('config', this.config);
89
99
 
100
+ // Configure Logger
101
+ if (this.config.logging) {
102
+ Logger.configure(this.config.logging);
103
+ }
104
+
90
105
  // Register plugins from config
91
106
  if (this.config.plugins) {
92
107
  this.config.plugins.forEach(plugin => {
@@ -0,0 +1,195 @@
1
+ /**
2
+ * PhoenixJS - Logger
3
+ *
4
+ * A NestJS-inspired logger service with color support, timestamps, and context.
5
+ */
6
+
7
+ export type LogLevel = 'log' | 'error' | 'warn' | 'debug' | 'verbose';
8
+
9
+ export class Logger {
10
+ private static instance: Logger;
11
+ private context?: string;
12
+ private static isDebugMode = process.env.NODE_ENV !== 'production';
13
+ private static isEnabled = true;
14
+
15
+ // ANSI Colors
16
+ private static readonly COLORS = {
17
+ reset: '\x1b[0m',
18
+ bright: '\x1b[1m',
19
+ dim: '\x1b[2m',
20
+ underscore: '\x1b[4m',
21
+ blink: '\x1b[5m',
22
+ reverse: '\x1b[7m',
23
+ hidden: '\x1b[8m',
24
+
25
+ fgBlack: '\x1b[30m',
26
+ fgRed: '\x1b[31m',
27
+ fgGreen: '\x1b[32m',
28
+ fgYellow: '\x1b[33m',
29
+ fgBlue: '\x1b[34m',
30
+ fgMagenta: '\x1b[35m',
31
+ fgCyan: '\x1b[36m',
32
+ fgWhite: '\x1b[37m',
33
+
34
+ bgBlack: '\x1b[40m',
35
+ bgRed: '\x1b[41m',
36
+ bgGreen: '\x1b[42m',
37
+ bgYellow: '\x1b[43m',
38
+ bgBlue: '\x1b[44m',
39
+ bgMagenta: '\x1b[45m',
40
+ bgCyan: '\x1b[46m',
41
+ bgWhite: '\x1b[47m',
42
+ };
43
+
44
+ constructor(context?: string) {
45
+ this.context = context;
46
+ }
47
+
48
+ /**
49
+ * Configure the logger
50
+ */
51
+ static configure(options: { enabled?: boolean }): void {
52
+ if (options.enabled !== undefined) {
53
+ Logger.isEnabled = options.enabled;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Set the logger context
59
+ */
60
+ setContext(context: string): void {
61
+ this.context = context;
62
+ }
63
+
64
+ /**
65
+ * Log a message (Level: LOG)
66
+ */
67
+ log(message: any, context?: string): void {
68
+ if (!Logger.isEnabled) return;
69
+ this.printMessage('log', message, context || this.context);
70
+ }
71
+
72
+ /**
73
+ * Log an error message (Level: ERROR)
74
+ */
75
+ error(message: any, trace?: string, context?: string): void {
76
+ if (!Logger.isEnabled) return;
77
+ this.printMessage('error', message, context || this.context, trace);
78
+ }
79
+
80
+ /**
81
+ * Log a warning message (Level: WARN)
82
+ */
83
+ warn(message: any, context?: string): void {
84
+ if (!Logger.isEnabled) return;
85
+ this.printMessage('warn', message, context || this.context);
86
+ }
87
+
88
+ /**
89
+ * Log a debug message (Level: DEBUG)
90
+ */
91
+ debug(message: any, context?: string): void {
92
+ if (!Logger.isEnabled || !Logger.isDebugMode) return;
93
+ this.printMessage('debug', message, context || this.context);
94
+ }
95
+
96
+ /**
97
+ * Log a verbose message (Level: VERBOSE)
98
+ */
99
+ verbose(message: any, context?: string): void {
100
+ if (!Logger.isEnabled || !Logger.isDebugMode) return;
101
+ this.printMessage('verbose', message, context || this.context);
102
+ }
103
+
104
+ /**
105
+ * Static method to log a message
106
+ */
107
+ static log(message: any, context?: string): void {
108
+ new Logger(context).log(message);
109
+ }
110
+
111
+ /**
112
+ * Static method to log an error
113
+ */
114
+ static error(message: any, trace?: string, context?: string): void {
115
+ new Logger(context).error(message, trace);
116
+ }
117
+
118
+ /**
119
+ * Static method to log a warning
120
+ */
121
+ static warn(message: any, context?: string): void {
122
+ new Logger(context).warn(message);
123
+ }
124
+
125
+ /**
126
+ * Static method to log debug
127
+ */
128
+ static debug(message: any, context?: string): void {
129
+ new Logger(context).debug(message);
130
+ }
131
+
132
+ /**
133
+ * Static method to log verbose
134
+ */
135
+ static verbose(message: any, context?: string): void {
136
+ new Logger(context).verbose(message);
137
+ }
138
+
139
+ /**
140
+ * Print the log message with formatting
141
+ */
142
+ private printMessage(level: LogLevel, message: any, context: string = 'System', trace?: string): void {
143
+ const timestamp = new Date().toLocaleString();
144
+ const pid = process.pid;
145
+ const color = this.getColor(level);
146
+ const reset = Logger.COLORS.reset;
147
+ const bright = Logger.COLORS.bright;
148
+ const dim = Logger.COLORS.dim;
149
+ const yellow = Logger.COLORS.fgYellow;
150
+
151
+ // Format: [AppName] PID - Timestamp LEVEL [Context] Message
152
+ // Example: [PhoenixJS] 1234 - 1/5/2026, 10:00:00 AM LOG [Router] Route registered
153
+
154
+ const appName = `[PhoenixJS]`;
155
+ const pidStr = `${pid}`;
156
+ const timeStr = timestamp;
157
+ const levelStr = level.toUpperCase().padEnd(7);
158
+ const contextStr = `[${context}]`;
159
+
160
+ let output = '';
161
+
162
+ // Green AppName
163
+ output += `${Logger.COLORS.fgGreen}${appName}${reset} `;
164
+ // Yellow PID
165
+ output += `${yellow}${pidStr}${reset} - `;
166
+ // White Timestamp
167
+ output += `${timeStr} `;
168
+ // Configured Color Level
169
+ output += `${color}${levelStr}${reset} `;
170
+ // Yellow Context
171
+ output += `${yellow}${contextStr}${reset} `;
172
+ // Message
173
+ output += `${color}${typeof message === 'object' ? JSON.stringify(message, null, 2) : message}${reset}`;
174
+
175
+ if (trace) {
176
+ output += `\n${Logger.COLORS.fgRed}${trace}${reset}`;
177
+ }
178
+
179
+ console.log(output);
180
+ }
181
+
182
+ /**
183
+ * Get ANSI color for log level
184
+ */
185
+ private getColor(level: LogLevel): string {
186
+ switch (level) {
187
+ case 'log': return Logger.COLORS.fgGreen;
188
+ case 'error': return Logger.COLORS.fgRed;
189
+ case 'warn': return Logger.COLORS.fgYellow;
190
+ case 'debug': return Logger.COLORS.fgMagenta;
191
+ case 'verbose': return Logger.COLORS.fgCyan;
192
+ default: return Logger.COLORS.fgWhite;
193
+ }
194
+ }
195
+ }
@@ -1,56 +1,34 @@
1
1
  import { Router } from '@framework/routing/Router';
2
2
  import { FrameworkResponse as Response } from '@framework/http/Response';
3
3
 
4
- /**
5
- * API Routes
6
- *
7
- * Define your API routes here. These routes are loaded by the kernel
8
- * and all routes here will have the /api prefix.
9
- */
10
4
  export function registerRoutes() {
11
- // Health check endpoint
12
- Router.get('/health', () => {
5
+
6
+ Router.get('/', () => {
13
7
  return Response.json({
14
- status: 'ok',
15
- timestamp: new Date().toISOString(),
8
+ name: 'PhoenixJS',
9
+ version: '0.1.0',
10
+ message: 'Welcome to PhoenixJS'
16
11
  });
17
12
  });
18
13
 
19
- // Welcome endpoint
20
- Router.get('/hello', () => {
14
+ Router.get('/health', () => {
21
15
  return Response.json({
22
- message: 'Welcome to PhoenixJS! 🚀',
16
+ status: 'healthy',
17
+ timestamp: new Date().toISOString(),
18
+ uptime: process.uptime()
23
19
  });
24
20
  });
25
21
 
26
- // Example API group with routes
22
+ // Sample API group
27
23
  Router.group({ prefix: '/api' }, () => {
28
- // Ping endpoint
29
24
  Router.get('/ping', () => Response.json({ message: 'pong' }));
30
25
 
31
- // Example with route parameter
32
26
  Router.get('/users/:id', () => {
33
- return Response.json({
34
- user: { id: '1', name: 'John Doe' },
35
- });
27
+ return Response.json({ id: '123' });
36
28
  });
37
29
 
38
- // Example POST route (use Controller for real implementations)
39
- Router.post('/users', () => {
40
- return Response.json({
41
- message: 'User created',
42
- }, 201);
30
+ Router.group({ prefix: '/v1' }, () => {
31
+ Router.get('/count', () => Response.json({ count: 1 }));
43
32
  });
44
33
  });
45
-
46
- /**
47
- * Route Groups Example
48
- *
49
- * You can use Controllers with the 'ControllerName@method' syntax:
50
- *
51
- * Router.group({ prefix: '/v1', middleware: [] }, () => {
52
- * Router.get('/products', 'ProductController@index');
53
- * Router.post('/products', 'ProductController@store');
54
- * });
55
- */
56
34
  }
@@ -1,61 +0,0 @@
1
- import { Controller } from '@framework/controller/Controller';
2
-
3
- /**
4
- * Example Controller
5
- *
6
- * This is a sample controller to get you started.
7
- * Generate new controllers using: bun artisan make:controller <name>
8
- */
9
- export class ExampleController extends Controller {
10
- /**
11
- * Display a listing of the resource.
12
- */
13
- async index() {
14
- return this.json({
15
- message: 'Welcome to PhoenixJS!',
16
- items: [],
17
- });
18
- }
19
-
20
- /**
21
- * Display the specified resource.
22
- */
23
- async show() {
24
- const id = this.param('id');
25
- return this.json({
26
- id,
27
- message: `Showing resource ${id}`,
28
- });
29
- }
30
-
31
- /**
32
- * Store a newly created resource.
33
- */
34
- async store() {
35
- const data = await this.request.json();
36
- return this.json({
37
- message: 'Resource created successfully',
38
- data,
39
- }, 201);
40
- }
41
-
42
- /**
43
- * Update the specified resource.
44
- */
45
- async update() {
46
- const id = this.param('id');
47
- const data = await this.request.json();
48
- return this.json({
49
- message: `Resource ${id} updated`,
50
- data,
51
- });
52
- }
53
-
54
- /**
55
- * Remove the specified resource.
56
- */
57
- async destroy() {
58
- const id = this.param('id');
59
- return this.noContent();
60
- }
61
- }