@scpxl/nodejs-framework 1.0.20 → 1.0.24
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/LICENSE +15 -0
- package/README.md +719 -66
- package/dist/application/base-application.d.ts.map +1 -1
- package/dist/application/base-application.interface.d.ts +1 -0
- package/dist/application/base-application.interface.d.ts.map +1 -1
- package/dist/application/base-application.js +4 -1
- package/dist/application/base-application.js.map +2 -2
- package/dist/application/command-application.d.ts.map +1 -1
- package/dist/application/command-application.js.map +2 -2
- package/dist/application/web-application.d.ts.map +1 -1
- package/dist/application/web-application.js +2 -0
- package/dist/application/web-application.js.map +2 -2
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +5655 -0
- package/dist/cli/index.js.map +7 -0
- package/dist/command/command.d.ts +1 -1
- package/dist/command/command.d.ts.map +1 -1
- package/dist/command/command.js.map +1 -1
- package/dist/config/schema.d.ts +40 -16
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +17 -7
- package/dist/config/schema.js.map +2 -2
- package/dist/event/manager.d.ts +2 -2
- package/dist/event/manager.d.ts.map +1 -1
- package/dist/event/manager.interface.d.ts +4 -1
- package/dist/event/manager.interface.d.ts.map +1 -1
- package/dist/event/manager.js.map +2 -2
- package/dist/lifecycle/lifecycle-manager.d.ts +1 -1
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -1
- package/dist/lifecycle/lifecycle-manager.js +6 -11
- package/dist/lifecycle/lifecycle-manager.js.map +2 -2
- package/dist/logger/logger.d.ts +4 -0
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/logger.js +29 -2
- package/dist/logger/logger.js.map +2 -2
- package/dist/queue/index.d.ts +1 -1
- package/dist/queue/index.d.ts.map +1 -1
- package/dist/queue/index.js.map +1 -1
- package/dist/queue/job.interface.d.ts +4 -3
- package/dist/queue/job.interface.d.ts.map +1 -1
- package/dist/queue/manager.d.ts +14 -6
- package/dist/queue/manager.d.ts.map +1 -1
- package/dist/queue/manager.js +24 -12
- package/dist/queue/manager.js.map +2 -2
- package/dist/queue/processor/base.d.ts +6 -5
- package/dist/queue/processor/base.d.ts.map +1 -1
- package/dist/queue/processor/base.js.map +2 -2
- package/dist/queue/processor/processor.interface.d.ts +4 -3
- package/dist/queue/processor/processor.interface.d.ts.map +1 -1
- package/dist/redis/manager.d.ts.map +1 -1
- package/dist/redis/manager.js +13 -14
- package/dist/redis/manager.js.map +2 -2
- package/dist/services/aws/s3.js.map +2 -2
- package/dist/util/helper.d.ts +9 -10
- package/dist/util/helper.d.ts.map +1 -1
- package/dist/util/helper.js +73 -11
- package/dist/util/helper.js.map +2 -2
- package/dist/util/loader.d.ts +8 -6
- package/dist/util/loader.d.ts.map +1 -1
- package/dist/util/loader.js +5 -2
- package/dist/util/loader.js.map +2 -2
- package/dist/util/timing.interface.d.ts +1 -1
- package/dist/util/timing.interface.d.ts.map +1 -1
- package/dist/webserver/controller/base.d.ts +9 -9
- package/dist/webserver/controller/base.d.ts.map +1 -1
- package/dist/webserver/controller/base.interface.d.ts +12 -9
- package/dist/webserver/controller/base.interface.d.ts.map +1 -1
- package/dist/webserver/controller/base.js.map +2 -2
- package/dist/webserver/define-action.d.ts +26 -0
- package/dist/webserver/define-action.d.ts.map +1 -0
- package/dist/webserver/define-action.js +16 -0
- package/dist/webserver/define-action.js.map +7 -0
- package/dist/webserver/define-route.d.ts +53 -0
- package/dist/webserver/define-route.d.ts.map +1 -0
- package/dist/webserver/define-route.js +27 -0
- package/dist/webserver/define-route.js.map +7 -0
- package/dist/webserver/index.d.ts +4 -2
- package/dist/webserver/index.d.ts.map +1 -1
- package/dist/webserver/index.js +4 -0
- package/dist/webserver/index.js.map +2 -2
- package/dist/webserver/util.d.ts.map +1 -1
- package/dist/webserver/util.js +5 -2
- package/dist/webserver/util.js.map +2 -2
- package/dist/webserver/webserver.d.ts +20 -6
- package/dist/webserver/webserver.d.ts.map +1 -1
- package/dist/webserver/webserver.interface.d.ts +30 -4
- package/dist/webserver/webserver.interface.d.ts.map +1 -1
- package/dist/webserver/webserver.interface.js.map +2 -2
- package/dist/webserver/webserver.js +209 -57
- package/dist/webserver/webserver.js.map +2 -2
- package/dist/websocket/websocket-base.d.ts +6 -2
- package/dist/websocket/websocket-base.d.ts.map +1 -1
- package/dist/websocket/websocket-base.js.map +2 -2
- package/dist/websocket/websocket-client.js.map +1 -1
- package/dist/websocket/websocket-server.d.ts.map +1 -1
- package/dist/websocket/websocket-server.js +38 -14
- package/dist/websocket/websocket-server.js.map +2 -2
- package/dist/websocket/websocket.interface.d.ts +9 -4
- package/dist/websocket/websocket.interface.d.ts.map +1 -1
- package/dist/websocket/websocket.interface.js.map +2 -2
- package/package.json +14 -13
- package/pxl.js +0 -4
package/README.md
CHANGED
|
@@ -1,120 +1,418 @@
|
|
|
1
1
|
# PXL Node.js Framework
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@scpxl/nodejs-framework)
|
|
4
|
+
[](https://nodejs.org)
|
|
5
|
+
[](https://opensource.org/licenses/ISC)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
A comprehensive, production-ready Node.js framework for building modern applications with built-in support for web servers, databases, queues, caching, WebSockets, and more.
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
**Opinionated TypeScript framework** combining Fastify, WebSockets, Redis, BullMQ, and MikroORM under a unified Application lifecycle with graceful shutdown, health checks, and observability.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## ✨ Features
|
|
15
|
+
|
|
16
|
+
### 🚀 **Core Application System**
|
|
17
|
+
|
|
18
|
+
- **Unified Lifecycle Management** - Coordinated startup, readiness probes, and graceful shutdown
|
|
19
|
+
- **TypeScript-First** - Full type safety with strict mode enabled and comprehensive type definitions
|
|
20
|
+
- **Configuration Validation** - Zod-based schema validation with fail-fast error reporting
|
|
21
|
+
- **Modular Architecture** - Use only what you need via granular package exports
|
|
22
|
+
|
|
23
|
+
### 🌐 **Web & Networking**
|
|
24
|
+
|
|
25
|
+
- **Fastify Web Server** - High-performance HTTP server with route management and middleware
|
|
26
|
+
- **Route Autoloading** - Drop route modules into a directory and have them loaded automatically
|
|
27
|
+
- **WebSocket Support** - Real-time bidirectional communication with room-based routing
|
|
28
|
+
- **CORS & Security** - Built-in CORS, Helmet integration, and rate limiting support
|
|
29
|
+
- **File Uploads** - Multipart form data handling with configurable limits
|
|
30
|
+
|
|
31
|
+
### 💾 **Data & State Management**
|
|
32
|
+
|
|
33
|
+
- **PostgreSQL + MikroORM** - Type-safe database access with migrations and entities
|
|
34
|
+
- **Redis Integration** - Connection pooling, pub/sub, and caching via `ioredis`
|
|
35
|
+
- **Queue Processing (BullMQ)** - Background job processing with Redis-backed queues
|
|
36
|
+
- **LRU Caching** - High-performance in-memory caching with TTL support
|
|
37
|
+
|
|
38
|
+
### 🔧 **Developer Experience**
|
|
39
|
+
|
|
40
|
+
- **Structured Logging** - Winston-based logging with context and Sentry integration
|
|
41
|
+
- **CLI Commands** - Yargs-based command system for scripts and utilities
|
|
42
|
+
- **Hot Module Reload** - Fast development iteration with automatic rebuilds
|
|
43
|
+
- **Request Context** - Trace requests across async boundaries with correlation IDs
|
|
44
|
+
- **Error Handling** - Standardized error classes with detailed context
|
|
45
|
+
|
|
46
|
+
### ⚙️ **Operations & Observability**
|
|
47
|
+
|
|
48
|
+
- **Health Endpoints** - Liveness (`/health/live`) and readiness (`/health/ready`) probes
|
|
49
|
+
- **Performance Monitoring** - Track connection health, queue metrics, and resource usage
|
|
50
|
+
- **Graceful Shutdown** - Coordinated cleanup of connections, intervals, and resources
|
|
51
|
+
- **Cluster Support** - Multi-process scaling with built-in cluster management
|
|
52
|
+
|
|
53
|
+
### 🔐 **Security & Authentication**
|
|
54
|
+
|
|
55
|
+
- **JWT Authentication** - JOSE-based token signing and verification
|
|
56
|
+
- **Input Validation** - Zod schemas with runtime validation and type inference
|
|
57
|
+
- **AWS S3 Integration** - Secure file storage with presigned URLs
|
|
58
|
+
- **Prototype Pollution Protection** - Safe object operations and property access
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 📦 Installation
|
|
8
63
|
|
|
9
64
|
```bash
|
|
10
65
|
npm install @scpxl/nodejs-framework
|
|
11
66
|
```
|
|
12
67
|
|
|
13
|
-
|
|
68
|
+
**Requirements:**
|
|
14
69
|
|
|
15
|
-
|
|
16
|
-
|
|
70
|
+
- Node.js >= 22.0.0
|
|
71
|
+
- PostgreSQL (optional, for database features)
|
|
72
|
+
- Redis (optional, for caching and queues)
|
|
17
73
|
|
|
18
|
-
|
|
19
|
-
webserver: { port: 3000 },
|
|
20
|
-
logger: { level: 'info' },
|
|
21
|
-
});
|
|
74
|
+
---
|
|
22
75
|
|
|
23
|
-
|
|
76
|
+
## 🚀 Quick Start
|
|
77
|
+
|
|
78
|
+
### Basic Web Application
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { WebApplication } from '@scpxl/nodejs-framework';
|
|
24
82
|
|
|
83
|
+
const app = new WebApplication({
|
|
84
|
+
name: 'my-app',
|
|
85
|
+
webserver: {
|
|
86
|
+
port: 3000,
|
|
87
|
+
host: '0.0.0.0',
|
|
88
|
+
},
|
|
89
|
+
redis: {
|
|
90
|
+
host: '127.0.0.1',
|
|
91
|
+
port: 6379,
|
|
92
|
+
},
|
|
93
|
+
logger: {
|
|
94
|
+
level: 'info',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Add routes
|
|
25
99
|
app.webserver.route({
|
|
26
100
|
method: 'GET',
|
|
27
|
-
url: '/health',
|
|
28
|
-
handler: async () =>
|
|
101
|
+
url: '/api/health',
|
|
102
|
+
handler: async (request, reply) => {
|
|
103
|
+
return { status: 'healthy', timestamp: new Date() };
|
|
104
|
+
},
|
|
29
105
|
});
|
|
106
|
+
|
|
107
|
+
// Start the application
|
|
108
|
+
await app.start();
|
|
109
|
+
|
|
110
|
+
console.log(`Server running at http://localhost:3000`);
|
|
30
111
|
```
|
|
31
112
|
|
|
32
|
-
|
|
113
|
+
### With Database & Queue
|
|
33
114
|
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
|
|
115
|
+
```typescript
|
|
116
|
+
import { WebApplication } from '@scpxl/nodejs-framework';
|
|
117
|
+
|
|
118
|
+
const app = new WebApplication({
|
|
119
|
+
name: 'my-app',
|
|
120
|
+
webserver: { port: 3000 },
|
|
121
|
+
database: {
|
|
122
|
+
enabled: true,
|
|
123
|
+
host: 'localhost',
|
|
124
|
+
port: 5432,
|
|
125
|
+
username: 'postgres',
|
|
126
|
+
password: 'password',
|
|
127
|
+
databaseName: 'myapp',
|
|
128
|
+
entitiesDirectory: './src/database/entities',
|
|
129
|
+
},
|
|
130
|
+
queue: {
|
|
131
|
+
enabled: true,
|
|
132
|
+
queues: [
|
|
133
|
+
{
|
|
134
|
+
id: 'email',
|
|
135
|
+
jobs: [{ id: 'send-welcome', processor: './src/processors/email-processor.ts' }],
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
redis: {
|
|
140
|
+
host: '127.0.0.1',
|
|
141
|
+
port: 6379,
|
|
142
|
+
},
|
|
37
143
|
});
|
|
144
|
+
|
|
145
|
+
await app.start();
|
|
146
|
+
|
|
147
|
+
// Add a job to the queue
|
|
148
|
+
await app.queue.manager.addJobToQueue({
|
|
149
|
+
queueId: 'email',
|
|
150
|
+
jobId: 'send-welcome',
|
|
151
|
+
data: { userId: 123, email: 'user@example.com' },
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Simple Load Test
|
|
156
|
+
|
|
157
|
+
Run lightweight load against any endpoint while iterating locally:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm run load:test -- --url http://localhost:3000/health --requests 200 --concurrency 10
|
|
38
161
|
```
|
|
39
162
|
|
|
40
|
-
|
|
163
|
+
Switch to a time-based stream for soak-style checks:
|
|
41
164
|
|
|
42
|
-
```
|
|
43
|
-
|
|
165
|
+
```bash
|
|
166
|
+
npm run load:test -- --url http://localhost:3000/api/users --duration 30 --concurrency 8 --method POST --body '{"name":"Test"}' --header 'Content-Type: application/json'
|
|
44
167
|
```
|
|
45
168
|
|
|
46
|
-
|
|
169
|
+
The script reports latency percentiles, status code counts, and a few failure samples for quick feedback.
|
|
170
|
+
|
|
171
|
+
### WebSocket Server
|
|
172
|
+
|
|
173
|
+
Real-time bidirectional communication with room support and authentication:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { WebApplication } from '@scpxl/nodejs-framework';
|
|
177
|
+
import { WebSocketServerBaseController } from '@scpxl/nodejs-framework/websocket';
|
|
178
|
+
import type { WebSocket } from 'ws';
|
|
179
|
+
|
|
180
|
+
// Create WebSocket controller
|
|
181
|
+
class ChatController extends WebSocketServerBaseController {
|
|
182
|
+
public send = (ws: WebSocket, clientId: string, data: any) => {
|
|
183
|
+
// Broadcast to all clients
|
|
184
|
+
this.webSocketServer.sendMessageToAll({
|
|
185
|
+
data: {
|
|
186
|
+
type: 'chat',
|
|
187
|
+
action: 'message',
|
|
188
|
+
data: {
|
|
189
|
+
clientId,
|
|
190
|
+
text: data.text,
|
|
191
|
+
timestamp: new Date().toISOString(),
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return { success: true };
|
|
197
|
+
};
|
|
198
|
+
}
|
|
47
199
|
|
|
48
|
-
|
|
49
|
-
|
|
200
|
+
const app = new WebApplication({
|
|
201
|
+
name: 'chat-app',
|
|
50
202
|
webserver: { port: 3000 },
|
|
51
|
-
websocket: {
|
|
52
|
-
|
|
203
|
+
websocket: {
|
|
204
|
+
enabled: true,
|
|
205
|
+
type: 'server',
|
|
206
|
+
url: 'ws://localhost:3000/ws',
|
|
207
|
+
controllersDirectory: './controllers',
|
|
208
|
+
routes: [
|
|
209
|
+
{
|
|
210
|
+
type: 'chat',
|
|
211
|
+
action: 'send',
|
|
212
|
+
controllerName: 'chat',
|
|
213
|
+
controller: ChatController,
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
// Optional: JWT authentication
|
|
217
|
+
// Clients connect with: ws://localhost:3000/ws?token=<jwt>
|
|
218
|
+
events: {
|
|
219
|
+
onConnected: ({ ws, clientId }) => {
|
|
220
|
+
console.log('Client connected:', clientId);
|
|
221
|
+
ws.send(
|
|
222
|
+
JSON.stringify({
|
|
223
|
+
type: 'system',
|
|
224
|
+
action: 'connected',
|
|
225
|
+
data: { clientId, message: 'Welcome!' },
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
53
231
|
redis: { host: '127.0.0.1', port: 6379 },
|
|
54
|
-
|
|
55
|
-
|
|
232
|
+
auth: {
|
|
233
|
+
jwtSecretKey: process.env.JWT_SECRET || 'your-secret-key',
|
|
56
234
|
},
|
|
57
|
-
logger: { level: 'info' },
|
|
58
235
|
});
|
|
236
|
+
|
|
237
|
+
await app.start();
|
|
238
|
+
console.log('WebSocket server running at ws://localhost:3000/ws');
|
|
59
239
|
```
|
|
60
240
|
|
|
61
|
-
|
|
241
|
+
#### Using Rooms
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// Client joins a room (built-in system controller)
|
|
245
|
+
// Send from client: { type: 'system', action: 'joinRoom', data: { roomName: 'general', username: 'Alice' } }
|
|
246
|
+
|
|
247
|
+
// Server-side: Broadcast to room members
|
|
248
|
+
const roomClients = app.websocket.server.rooms.get('general');
|
|
249
|
+
roomClients?.forEach(clientId => {
|
|
250
|
+
const client = app.websocket.server.clientManager.getClient({ clientId });
|
|
251
|
+
if (client?.ws) {
|
|
252
|
+
app.websocket.server.sendClientMessage(client.ws, {
|
|
253
|
+
type: 'room',
|
|
254
|
+
action: 'message',
|
|
255
|
+
data: { text: 'Room-specific announcement' },
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
```
|
|
62
260
|
|
|
63
|
-
|
|
64
|
-
- Fastify routing + raw access
|
|
65
|
-
- WebSocket client + room management
|
|
66
|
-
- BullMQ queue integration
|
|
67
|
-
- MikroORM database integration
|
|
68
|
-
- Redis cache + pub/sub
|
|
69
|
-
- Structured logging
|
|
70
|
-
- Utilities & services layer
|
|
261
|
+
#### Using WebSocket Service
|
|
71
262
|
|
|
72
|
-
|
|
263
|
+
Simplified API for common operations:
|
|
73
264
|
|
|
74
|
-
|
|
265
|
+
```typescript
|
|
266
|
+
import { WebSocketService } from '@scpxl/nodejs-framework/websocket';
|
|
75
267
|
|
|
76
|
-
|
|
77
|
-
|
|
268
|
+
const wsService = new WebSocketService({
|
|
269
|
+
webSocketServer: app.websocket.server,
|
|
270
|
+
redisInstance: app.redis.instance,
|
|
271
|
+
workerId: String(process.pid),
|
|
272
|
+
});
|
|
78
273
|
|
|
79
|
-
|
|
274
|
+
// Broadcast to all clients
|
|
275
|
+
await wsService.broadcast({
|
|
276
|
+
type: 'notification',
|
|
277
|
+
action: 'alert',
|
|
278
|
+
data: { message: 'New features available!' },
|
|
279
|
+
});
|
|
80
280
|
|
|
81
|
-
|
|
82
|
-
|
|
281
|
+
// Send to specific rooms
|
|
282
|
+
await wsService.sendToRooms(['vip', 'premium'], {
|
|
283
|
+
type: 'offer',
|
|
284
|
+
action: 'new',
|
|
285
|
+
data: { discount: 20 },
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Convenience methods
|
|
289
|
+
await wsService.sendUserMessage('profileUpdated', { userId: 123 });
|
|
290
|
+
await wsService.sendSystemMessage('maintenance', { minutes: 5 });
|
|
83
291
|
```
|
|
84
292
|
|
|
85
|
-
|
|
293
|
+
See the [WebSocket Guide](./docs/guides/websocket.md) for complete documentation.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## 📚 Documentation
|
|
298
|
+
|
|
299
|
+
### Architecture
|
|
300
|
+
|
|
301
|
+
The framework is built around three main application types:
|
|
302
|
+
|
|
303
|
+
1. **`BaseApplication`** - Abstract base with Redis, Database, Queue, Events, Performance Monitoring
|
|
304
|
+
2. **`WebApplication`** - Extends `BaseApplication` with Fastify web server and WebSocket support
|
|
305
|
+
3. **`CommandApplication`** - Extends `BaseApplication` for CLI commands and scripts
|
|
306
|
+
|
|
307
|
+
### Core Components
|
|
308
|
+
|
|
309
|
+
| Component | Description | Import Path |
|
|
310
|
+
| ------------------- | ------------------------------------------------ | ----------------------------------------- |
|
|
311
|
+
| **Application** | Main application classes | `@scpxl/nodejs-framework/application` |
|
|
312
|
+
| **Logger** | Structured logging with Winston | `@scpxl/nodejs-framework/logger` |
|
|
313
|
+
| **Database** | MikroORM integration and entity management | `@scpxl/nodejs-framework/database` |
|
|
314
|
+
| **WebServer** | Fastify server and routing | `@scpxl/nodejs-framework/webserver` |
|
|
315
|
+
| **WebSocket** | WebSocket server and client | `@scpxl/nodejs-framework/websocket` |
|
|
316
|
+
| **Queue** | BullMQ job queue management | `@scpxl/nodejs-framework/queue` |
|
|
317
|
+
| **Redis** | Redis connection management | `@scpxl/nodejs-framework/redis` |
|
|
318
|
+
| **Cache** | High-level caching abstraction | `@scpxl/nodejs-framework/cache` |
|
|
319
|
+
| **Auth** | JWT authentication utilities | `@scpxl/nodejs-framework/auth` |
|
|
320
|
+
| **Request Context** | Request correlation and tracing | `@scpxl/nodejs-framework/request-context` |
|
|
321
|
+
| **Lifecycle** | Application lifecycle and shutdown management | `@scpxl/nodejs-framework/lifecycle` |
|
|
322
|
+
| **Error** | Custom error classes | `@scpxl/nodejs-framework/error` |
|
|
323
|
+
| **Utilities** | File, string, time, URL helpers | `@scpxl/nodejs-framework/util` |
|
|
324
|
+
| **Performance** | Performance monitoring and metrics | `@scpxl/nodejs-framework/performance` |
|
|
325
|
+
| **API Requester** | HTTP client wrapper (migrated to native `fetch`) | `@scpxl/nodejs-framework/api-requester` |
|
|
326
|
+
| **Command** | CLI command framework | `@scpxl/nodejs-framework/command` |
|
|
327
|
+
| **Services** | Additional service integrations (AWS S3, etc.) | `@scpxl/nodejs-framework/services` |
|
|
328
|
+
|
|
329
|
+
### Key Patterns
|
|
330
|
+
|
|
331
|
+
#### Lifecycle Hooks
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
const app = new WebApplication(config);
|
|
335
|
+
|
|
336
|
+
// Register lifecycle hooks
|
|
337
|
+
app.lifecycle.onStart(async () => {
|
|
338
|
+
console.log('Application starting...');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
app.lifecycle.onReady(async () => {
|
|
342
|
+
console.log('Application ready for traffic');
|
|
343
|
+
});
|
|
86
344
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
345
|
+
app.lifecycle.onShutdown(async () => {
|
|
346
|
+
console.log('Cleaning up resources...');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
await app.start();
|
|
90
350
|
```
|
|
91
351
|
|
|
92
|
-
|
|
352
|
+
#### Graceful Shutdown
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
const app = new WebApplication(config);
|
|
356
|
+
|
|
357
|
+
await app.start();
|
|
358
|
+
|
|
359
|
+
// Handle signals
|
|
360
|
+
process.on('SIGINT', async () => {
|
|
361
|
+
console.log('Received SIGINT, shutting down gracefully...');
|
|
362
|
+
await app.stop();
|
|
363
|
+
process.exit(0);
|
|
364
|
+
});
|
|
93
365
|
|
|
94
|
-
|
|
366
|
+
process.on('SIGTERM', async () => {
|
|
367
|
+
console.log('Received SIGTERM, shutting down gracefully...');
|
|
368
|
+
await app.stop();
|
|
369
|
+
process.exit(0);
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### Service Injection Pattern
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
95
376
|
class UserService {
|
|
96
|
-
constructor(private app:
|
|
97
|
-
|
|
98
|
-
|
|
377
|
+
constructor(private app: WebApplication) {}
|
|
378
|
+
|
|
379
|
+
async createUser(data: CreateUserDto) {
|
|
380
|
+
// Use database
|
|
381
|
+
const user = this.app.database.instance.em.create(User, data);
|
|
382
|
+
await this.app.database.instance.em.flush();
|
|
383
|
+
|
|
384
|
+
// Use queue
|
|
385
|
+
await this.app.queue.manager.addJobToQueue({
|
|
386
|
+
queueId: 'email',
|
|
387
|
+
jobId: 'send-welcome',
|
|
388
|
+
data: { userId: user.id },
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Use logger
|
|
392
|
+
this.app.logger.info('User created', { userId: user.id });
|
|
393
|
+
|
|
394
|
+
return user;
|
|
99
395
|
}
|
|
100
396
|
}
|
|
101
397
|
```
|
|
102
398
|
|
|
103
|
-
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## 📖 Examples
|
|
104
402
|
|
|
105
|
-
The `examples/` directory contains working
|
|
403
|
+
The `examples/` directory contains working demonstrations:
|
|
106
404
|
|
|
107
405
|
### Hello World Example
|
|
108
406
|
|
|
109
|
-
A
|
|
407
|
+
A full-stack example with:
|
|
110
408
|
|
|
111
|
-
- Backend
|
|
112
|
-
- Frontend
|
|
409
|
+
- **Backend**: PXL WebApplication with TypeScript, WebSocket support, and API routes
|
|
410
|
+
- **Frontend**: Vue 3 + TypeScript + Vite with real-time WebSocket updates
|
|
113
411
|
|
|
114
412
|
**Run the example:**
|
|
115
413
|
|
|
116
414
|
```bash
|
|
117
|
-
# Install dependencies for
|
|
415
|
+
# Install dependencies for examples (one-time setup)
|
|
118
416
|
npm run example:install
|
|
119
417
|
|
|
120
418
|
# Run backend + frontend together with hot-reload
|
|
@@ -127,21 +425,376 @@ npm run example:hello-world:frontend
|
|
|
127
425
|
|
|
128
426
|
Then open http://localhost:5173 to see the app.
|
|
129
427
|
|
|
428
|
+
### CLI Commands Example
|
|
429
|
+
|
|
430
|
+
Demonstrates the command framework with examples:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
# Install dependencies
|
|
434
|
+
npm run example:commands:install
|
|
435
|
+
|
|
436
|
+
# Run hello command
|
|
437
|
+
npm run example:commands:hello
|
|
438
|
+
|
|
439
|
+
# Run database seed command
|
|
440
|
+
npm run example:commands:seed
|
|
441
|
+
|
|
442
|
+
# Run queue processing command
|
|
443
|
+
npm run example:commands:queue
|
|
444
|
+
```
|
|
445
|
+
|
|
130
446
|
See [examples/README.md](examples/README.md) for more details.
|
|
131
447
|
|
|
132
|
-
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## 🛠️ Development
|
|
451
|
+
|
|
452
|
+
### CLI (`pxl`)
|
|
453
|
+
|
|
454
|
+
The framework now ships with a bundled CLI executable exposed as `pxl` when the package is installed.
|
|
133
455
|
|
|
134
|
-
|
|
456
|
+
Current capabilities:
|
|
135
457
|
|
|
136
|
-
|
|
458
|
+
- `pxl --version` / `pxl -v` / `pxl version` – Print framework version
|
|
459
|
+
- `pxl info` (or just `pxl`) – Display banner + roadmap
|
|
137
460
|
|
|
138
|
-
|
|
461
|
+
Planned subcommands (roadmap):
|
|
462
|
+
|
|
463
|
+
- `pxl doctor` – Environment diagnostics (Node version, dependency checks, Redis/Postgres availability)
|
|
464
|
+
- `pxl generate` – Scaffolding for applications, routes, commands, processors
|
|
465
|
+
- `pxl analyze` – Project inspection (unused files, dependency graph summary)
|
|
466
|
+
|
|
467
|
+
Usage examples:
|
|
139
468
|
|
|
140
469
|
```bash
|
|
141
|
-
|
|
142
|
-
|
|
470
|
+
# Show version
|
|
471
|
+
pxl --version
|
|
472
|
+
|
|
473
|
+
# Show framework banner and roadmap
|
|
474
|
+
pxl info
|
|
475
|
+
|
|
476
|
+
# (Future) Run doctor diagnostics
|
|
477
|
+
pxl doctor
|
|
143
478
|
```
|
|
144
479
|
|
|
480
|
+
Development Note:
|
|
481
|
+
|
|
482
|
+
When iterating locally, rebuild after CLI changes:
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
npm run build && pxl info
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
To test unpublished changes in another project via yalc:
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
npm run build:local
|
|
492
|
+
yalc add @scpxl/nodejs-framework
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
For contributions adding new subcommands, implement them in `src/cli/` and register via the yargs builder in `src/cli/index.ts`.
|
|
496
|
+
|
|
497
|
+
### Build Commands
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
# Development with hot-reload
|
|
501
|
+
npm run dev
|
|
502
|
+
|
|
503
|
+
# Production build
|
|
504
|
+
npm run build
|
|
505
|
+
|
|
506
|
+
# Type checking
|
|
507
|
+
npm run typecheck
|
|
508
|
+
|
|
509
|
+
# Linting
|
|
510
|
+
npm run lint
|
|
511
|
+
npm run lint:fix
|
|
512
|
+
|
|
513
|
+
# Code formatting
|
|
514
|
+
npm run prettier
|
|
515
|
+
npm run prettier:fix
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Testing
|
|
519
|
+
|
|
520
|
+
```bash
|
|
521
|
+
# Run all tests
|
|
522
|
+
npm test
|
|
523
|
+
|
|
524
|
+
# Run tests in watch mode
|
|
525
|
+
npm run test:watch
|
|
526
|
+
|
|
527
|
+
# Run with coverage report
|
|
528
|
+
npm run test:coverage
|
|
529
|
+
|
|
530
|
+
# Run specific test suites
|
|
531
|
+
npm run test:unit
|
|
532
|
+
npm run test:integration
|
|
533
|
+
npm run test:e2e
|
|
534
|
+
|
|
535
|
+
# Run tests with UI
|
|
536
|
+
npm run test:ui
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
The framework maintains **80% code coverage** across all metrics (lines, branches, functions, statements) as enforced by Vitest thresholds.
|
|
540
|
+
|
|
541
|
+
### Framework Status Report
|
|
542
|
+
|
|
543
|
+
Generate a cross-platform project health snapshot (dependency counts, git status, directory sizes, large packages, outdated/age metrics):
|
|
544
|
+
|
|
545
|
+
```bash
|
|
546
|
+
npm run status
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Optional flags:
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
npm run status -- --include-cache # Include .turbo/ and .next/cache directories
|
|
553
|
+
npm run status -- --exclude "coverage,fixtures,**/*.snap" # Additional exclude globs (comma-separated)
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
What it does:
|
|
557
|
+
|
|
558
|
+
- Collects repo & package metadata (version, scripts, dependency counts)
|
|
559
|
+
- Summarizes current git branch, last commit, and pending change counts
|
|
560
|
+
- Computes sizes for `src`, `dist`, and `node_modules` using fast native traversal (`fast-folder-size`) with a JS fallback, filtering via `.gitignore` / `.npmignore` plus exclusions
|
|
561
|
+
- Lists the largest packages in `node_modules` (top 8 by size)
|
|
562
|
+
- Ranks outdated dependencies by publish age and major version lag (uses `npm outdated` / `npm view`)
|
|
563
|
+
- Provides age distribution stats for installed top-level packages
|
|
564
|
+
|
|
565
|
+
Exclusions & Ignore Behavior:
|
|
566
|
+
|
|
567
|
+
- Always respects patterns found in `.gitignore` and `.npmignore` (except always keeps top-level `dist` & `node_modules`)
|
|
568
|
+
- Built-in default excludes: `coverage/`, `fixtures/`, and `**/*.snap`
|
|
569
|
+
- Skips heavy build caches (`.turbo`, `.next/cache`) unless `--include-cache` is passed
|
|
570
|
+
|
|
571
|
+
Return codes: exits with non-zero only on unexpected internal errors; missing npm or network failures simply degrade sections gracefully.
|
|
572
|
+
|
|
573
|
+
### Local Development with Yalc
|
|
574
|
+
|
|
575
|
+
For testing changes in consuming applications:
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
# Publish framework locally
|
|
579
|
+
npm run build:local
|
|
580
|
+
|
|
581
|
+
# In your consuming project
|
|
582
|
+
yalc add @scpxl/nodejs-framework
|
|
583
|
+
|
|
584
|
+
# Push updates after changes
|
|
585
|
+
npm run yalc:push
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## 🔧 Configuration
|
|
591
|
+
|
|
592
|
+
### Environment Variables
|
|
593
|
+
|
|
594
|
+
Create a `.env` file in your project root:
|
|
595
|
+
|
|
596
|
+
```env
|
|
597
|
+
# Application
|
|
598
|
+
NODE_ENV=development
|
|
599
|
+
APP_NAME=my-app
|
|
600
|
+
APP_PORT=3000
|
|
601
|
+
|
|
602
|
+
# Database
|
|
603
|
+
DB_HOST=localhost
|
|
604
|
+
DB_PORT=5432
|
|
605
|
+
DB_USERNAME=postgres
|
|
606
|
+
DB_PASSWORD=password
|
|
607
|
+
DB_NAME=myapp
|
|
608
|
+
|
|
609
|
+
# Redis
|
|
610
|
+
REDIS_HOST=127.0.0.1
|
|
611
|
+
REDIS_PORT=6379
|
|
612
|
+
REDIS_PASSWORD=
|
|
613
|
+
|
|
614
|
+
# Logging
|
|
615
|
+
LOG_LEVEL=info
|
|
616
|
+
|
|
617
|
+
# Sentry (optional)
|
|
618
|
+
SENTRY_DSN=https://...
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### TypeScript Configuration
|
|
622
|
+
|
|
623
|
+
The framework uses **ESNext** module target with `.js` extensions in imports:
|
|
624
|
+
|
|
625
|
+
```typescript
|
|
626
|
+
// ✅ Correct
|
|
627
|
+
import { WebApplication } from '@scpxl/nodejs-framework/application';
|
|
628
|
+
|
|
629
|
+
// ✅ Also correct (in framework source)
|
|
630
|
+
import { Logger } from '../logger/index.js';
|
|
631
|
+
|
|
632
|
+
// ❌ Incorrect (in framework source)
|
|
633
|
+
import { Logger } from '../logger'; // Missing .js extension
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
## 🏗️ Architecture Overview
|
|
639
|
+
|
|
640
|
+
```
|
|
641
|
+
┌─────────────────────────────────────────────────────────┐
|
|
642
|
+
│ Application Layer │
|
|
643
|
+
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
|
|
644
|
+
│ │ Web │ │ Command │ │ Custom App │ │
|
|
645
|
+
│ │ Application │ │ Application │ │ │ │
|
|
646
|
+
│ └──────┬──────┘ └──────┬───────┘ └───────┬───────┘ │
|
|
647
|
+
└─────────┼─────────────────┼──────────────────┼──────────┘
|
|
648
|
+
│ │ │
|
|
649
|
+
└─────────────────┴──────────────────┘
|
|
650
|
+
│
|
|
651
|
+
┌─────────────────▼──────────────────┐
|
|
652
|
+
│ Base Application │
|
|
653
|
+
│ ┌──────────────────────────────┐ │
|
|
654
|
+
│ │ Lifecycle Manager │ │
|
|
655
|
+
│ │ - Startup phases │ │
|
|
656
|
+
│ │ - Readiness probes │ │
|
|
657
|
+
│ │ - Graceful shutdown │ │
|
|
658
|
+
│ └──────────────────────────────┘ │
|
|
659
|
+
└────────────────┬───────────────────┘
|
|
660
|
+
│
|
|
661
|
+
┌─────────────────────┼─────────────────────┐
|
|
662
|
+
│ │ │
|
|
663
|
+
┌────▼─────┐ ┌────────▼────────┐ ┌───────▼──────┐
|
|
664
|
+
│ Redis │ │ Database │ │ Queue │
|
|
665
|
+
│ Manager │◄─────┤ Manager │ │ Manager │
|
|
666
|
+
└────┬─────┘ └────────┬────────┘ └───────┬──────┘
|
|
667
|
+
│ │ │
|
|
668
|
+
┌────▼─────┐ ┌────────▼────────┐ ┌───────▼──────┐
|
|
669
|
+
│ Cache │ │ MikroORM │ │ BullMQ │
|
|
670
|
+
│ Manager │ │ PostgreSQL │ │ Workers │
|
|
671
|
+
└──────────┘ └─────────────────┘ └──────────────┘
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
---
|
|
675
|
+
|
|
676
|
+
## 🎯 When to Use PXL Framework
|
|
677
|
+
|
|
678
|
+
### ✅ **Good Fit**
|
|
679
|
+
|
|
680
|
+
- Building **APIs** or **microservices** with TypeScript
|
|
681
|
+
- Need **real-time features** via WebSockets
|
|
682
|
+
- Require **background job processing** with queues
|
|
683
|
+
- Want **structured application lifecycle** with health checks
|
|
684
|
+
- Building **full-stack applications** with unified backend framework
|
|
685
|
+
- Need **production-ready** defaults with observability built-in
|
|
686
|
+
|
|
687
|
+
### ⚠️ **Consider Alternatives**
|
|
688
|
+
|
|
689
|
+
- **Simple scripts** or **single-purpose utilities** - Framework may be heavier than needed
|
|
690
|
+
- **Serverless functions** - Better suited for lightweight frameworks
|
|
691
|
+
- **Non-TypeScript projects** - Framework is TypeScript-first
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
## 🤝 Contributing
|
|
696
|
+
|
|
697
|
+
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for:
|
|
698
|
+
|
|
699
|
+
- Development setup instructions
|
|
700
|
+
- Code style guidelines
|
|
701
|
+
- Testing requirements
|
|
702
|
+
- Pull request process
|
|
703
|
+
|
|
704
|
+
### Quick Contribution Guide
|
|
705
|
+
|
|
706
|
+
1. Fork the repository
|
|
707
|
+
2. Create a feature branch: `git checkout -b feature/my-feature`
|
|
708
|
+
3. Make your changes with tests
|
|
709
|
+
4. Run checks: `npm run check-all` (linting, prettier, typecheck)
|
|
710
|
+
5. Ensure tests pass: `npm test`
|
|
711
|
+
6. Commit with descriptive message
|
|
712
|
+
7. Push and create a Pull Request
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## 🐛 Troubleshooting
|
|
717
|
+
|
|
718
|
+
### Database Connection Issues
|
|
719
|
+
|
|
720
|
+
```
|
|
721
|
+
Error: Connection to database failed
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
**Solution**: Ensure PostgreSQL is running and credentials are correct in `.env`
|
|
725
|
+
|
|
726
|
+
```bash
|
|
727
|
+
# Check PostgreSQL status
|
|
728
|
+
docker ps | grep postgres
|
|
729
|
+
|
|
730
|
+
# Start PostgreSQL with Docker
|
|
731
|
+
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=password postgres:16
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### Redis Connection Errors
|
|
735
|
+
|
|
736
|
+
```
|
|
737
|
+
Error: Redis connection refused
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
**Solution**: Ensure Redis is running
|
|
741
|
+
|
|
742
|
+
```bash
|
|
743
|
+
# Start Redis with Docker
|
|
744
|
+
docker run -d -p 6379:6379 redis:7-alpine
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### Port Already in Use
|
|
748
|
+
|
|
749
|
+
```
|
|
750
|
+
Error: listen EADDRINUSE: address already in use :::3000
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
**Solution**: Change the port in configuration or kill the process using the port
|
|
754
|
+
|
|
755
|
+
```bash
|
|
756
|
+
# Find process using port 3000
|
|
757
|
+
lsof -i :3000
|
|
758
|
+
|
|
759
|
+
# Kill the process
|
|
760
|
+
kill -9 <PID>
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### TypeScript Module Resolution
|
|
764
|
+
|
|
765
|
+
```
|
|
766
|
+
Error: Cannot find module '../logger/index.js'
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**Solution**: Ensure all imports in framework source code use `.js` extensions (required for ESM)
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
## 📄 License
|
|
774
|
+
|
|
775
|
+
[ISC License](LICENSE) - Copyright (c) PXL Agency
|
|
776
|
+
|
|
777
|
+
---
|
|
778
|
+
|
|
779
|
+
## 🔗 Links
|
|
780
|
+
|
|
781
|
+
- **Documentation**: https://pxlbros.github.io/pxl-nodejs-framework/
|
|
782
|
+
- **npm Package**: https://www.npmjs.com/package/@scpxl/nodejs-framework
|
|
783
|
+
- **GitHub Repository**: https://github.com/pxlbros/pxl-nodejs-framework
|
|
784
|
+
- **Issues**: https://github.com/pxlbros/pxl-nodejs-framework/issues
|
|
785
|
+
- **Changelog**: [CHANGELOG.md](CHANGELOG.md)
|
|
786
|
+
- **TODO/Roadmap**: [TODO.md](TODO.md)
|
|
787
|
+
|
|
788
|
+
---
|
|
789
|
+
|
|
790
|
+
## 💬 Support
|
|
791
|
+
|
|
792
|
+
For questions, issues, or feature requests:
|
|
793
|
+
|
|
794
|
+
- Open an [issue on GitHub](https://github.com/pxlbros/pxl-nodejs-framework/issues)
|
|
795
|
+
- Check existing [discussions](https://github.com/pxlbros/pxl-nodejs-framework/discussions)
|
|
796
|
+
- Review [documentation](https://pxlbros.github.io/pxl-nodejs-framework/)
|
|
797
|
+
|
|
145
798
|
---
|
|
146
799
|
|
|
147
|
-
|
|
800
|
+
**Built with ❤️ by [PXL Agency](https://pxlagency.com)**
|