ioserver 2.1.0 โ 2.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/README.md +322 -164
- package/dist/BaseClasses.d.ts +1 -1
- package/dist/BaseClasses.js +1 -1
- package/dist/IOServer.d.ts +1 -1
- package/dist/IOServer.js +2 -2
- package/dist/IOServerError.d.ts +1 -1
- package/dist/IOServerError.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +37 -36
package/README.md
CHANGED
|
@@ -1,266 +1,424 @@
|
|
|
1
|
-
#
|
|
1
|
+
# IOServer
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/ioserver)
|
|
4
4
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
5
|
-
[](https://github.com/x42en/IOServer/actions)
|
|
6
6
|
[](https://codecov.io/gh/x42en/IOServer)
|
|
7
|
-
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
A TypeScript framework for building real-time applications, combining [Fastify](https://fastify.dev/) (HTTP) and [Socket.IO](https://socket.io/) (WebSocket) behind a single unified API.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
IOServer structures your application around five component types โ Services, Controllers, Managers, Watchers, and Middlewares โ each with a well-defined responsibility. Components are registered on the IOServer instance before startup; the framework wires routing, CORS, and Socket.IO transport automatically.
|
|
14
|
+
|
|
15
|
+
It is designed to be small, explicit, and easily testable:
|
|
16
|
+
|
|
17
|
+
- No magic decorators or code generation
|
|
18
|
+
- Route definitions are plain JSON files, kept separate from handler logic
|
|
19
|
+
- Managers are injectable singletons available to every component via `AppHandle`
|
|
20
|
+
- All base classes expose a minimal surface; you only override what you need
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
```mermaid
|
|
25
|
+
graph TB
|
|
26
|
+
subgraph "Clients"
|
|
27
|
+
HTTP[HTTP Clients]
|
|
28
|
+
WS[WebSocket Clients]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
subgraph "IOServer"
|
|
32
|
+
direction TB
|
|
33
|
+
Fastify[Fastify HTTP layer]
|
|
34
|
+
SocketIO[Socket.IO layer]
|
|
35
|
+
|
|
36
|
+
subgraph "Components"
|
|
37
|
+
MW[Middlewares]
|
|
38
|
+
CTL[Controllers]
|
|
39
|
+
SVC[Services]
|
|
40
|
+
MGR[Managers]
|
|
41
|
+
WCH[Watchers]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
HTTP -->|REST requests| Fastify
|
|
46
|
+
WS -->|WS upgrade| SocketIO
|
|
47
|
+
|
|
48
|
+
Fastify --> MW
|
|
49
|
+
SocketIO --> MW
|
|
50
|
+
MW --> CTL
|
|
51
|
+
MW --> SVC
|
|
52
|
+
CTL --> MGR
|
|
53
|
+
SVC --> MGR
|
|
54
|
+
WCH --> MGR
|
|
55
|
+
```
|
|
10
56
|
|
|
11
|
-
|
|
57
|
+
## Key Features
|
|
12
58
|
|
|
13
|
-
|
|
59
|
+
- **Unified HTTP + WebSocket** โ Fastify v5 and Socket.IO v4 share the same port and TLS configuration
|
|
60
|
+
- **Component model** โ Five explicit roles (Service, Controller, Manager, Watcher, Middleware) keep business logic isolated and testable
|
|
61
|
+
- **JSON route files** โ HTTP routes are declared in `.json` files; no annotations or meta-programming required
|
|
62
|
+
- **Injectable managers** โ Singleton managers are exposed to all components through a typed `AppHandle`, avoiding global state
|
|
63
|
+
- **TypeScript native** โ Ships with declaration files; strict mode compatible
|
|
64
|
+
- **CORS built-in** โ Pass a standard Fastify CORS options object; applied to both HTTP and Socket.IO handshake
|
|
65
|
+
- **Configurable transports** โ Choose `websocket`, `polling`, or both for Socket.IO
|
|
66
|
+
- **SPA fallback** โ Optional static file serving with single-page application fallback routing
|
|
14
67
|
|
|
15
|
-
|
|
16
|
-
- โก **Real-time Communication** - Integrated Socket.IO for WebSocket connections
|
|
17
|
-
- ๐๏ธ **Modular Architecture** - Clean separation with Services, Controllers, Managers, and Watchers
|
|
18
|
-
- ๐ **Security First** - Built-in CORS, error handling, and validation
|
|
19
|
-
- ๐ **TypeScript Native** - Full type safety and IntelliSense support
|
|
20
|
-
- ๐งช **Fully Tested** - Comprehensive test suite with 95%+ coverage
|
|
21
|
-
- ๐ง **Configuration Driven** - JSON-based routing and flexible configuration
|
|
22
|
-
- ๐ฆ **Production Ready** - Memory leak detection, performance monitoring, and error handling
|
|
68
|
+
## Requirements
|
|
23
69
|
|
|
24
|
-
|
|
70
|
+
- Node.js 18+
|
|
71
|
+
- TypeScript 5.0+
|
|
72
|
+
- pnpm (recommended) or npm / yarn
|
|
25
73
|
|
|
26
|
-
|
|
74
|
+
## Installation
|
|
27
75
|
|
|
28
76
|
```bash
|
|
29
77
|
npm install ioserver
|
|
30
78
|
# or
|
|
31
|
-
|
|
79
|
+
pnpm add ioserver
|
|
32
80
|
```
|
|
33
81
|
|
|
34
|
-
|
|
82
|
+
## Quick Start
|
|
35
83
|
|
|
36
84
|
```typescript
|
|
37
|
-
import { IOServer, BaseService, BaseController } from 'ioserver';
|
|
85
|
+
import { IOServer, BaseService, BaseController, BaseManager } from 'ioserver';
|
|
38
86
|
|
|
39
|
-
//
|
|
87
|
+
// --- Manager: shared state ---
|
|
88
|
+
class AppManager extends BaseManager {
|
|
89
|
+
private count = 0;
|
|
90
|
+
increment() { this.count++; }
|
|
91
|
+
getCount() { return this.count; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// --- Service: WebSocket events ---
|
|
40
95
|
class ChatService extends BaseService {
|
|
41
|
-
async sendMessage(socket: any, data:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
96
|
+
async sendMessage(socket: any, data: { text: string }, callback?: Function) {
|
|
97
|
+
const mgr = this.app.getManager('app') as AppManager;
|
|
98
|
+
mgr.increment();
|
|
99
|
+
socket.broadcast.emit('message', { text: data.text, total: mgr.getCount() });
|
|
100
|
+
if (callback) callback({ status: 'ok' });
|
|
45
101
|
}
|
|
46
102
|
}
|
|
47
103
|
|
|
48
|
-
//
|
|
49
|
-
class
|
|
50
|
-
async
|
|
51
|
-
|
|
104
|
+
// --- Controller: HTTP endpoints ---
|
|
105
|
+
class StatsController extends BaseController {
|
|
106
|
+
async getStats(request: any, reply: any) {
|
|
107
|
+
const mgr = this.app.getManager('app') as AppManager;
|
|
108
|
+
reply.send({ messages: mgr.getCount() });
|
|
52
109
|
}
|
|
53
110
|
}
|
|
54
111
|
|
|
55
|
-
//
|
|
56
|
-
const server = new IOServer({
|
|
57
|
-
host: 'localhost',
|
|
58
|
-
port: 3000,
|
|
59
|
-
cors: {
|
|
60
|
-
origin: ['http://localhost:3000'],
|
|
61
|
-
methods: ['GET', 'POST'],
|
|
62
|
-
},
|
|
63
|
-
});
|
|
112
|
+
// --- Bootstrap ---
|
|
113
|
+
const server = new IOServer({ host: 'localhost', port: 3000 });
|
|
64
114
|
|
|
65
|
-
|
|
115
|
+
server.addManager({ name: 'app', manager: AppManager });
|
|
66
116
|
server.addService({ name: 'chat', service: ChatService });
|
|
67
|
-
server.addController({ name: '
|
|
117
|
+
server.addController({ name: 'stats', controller: StatsController });
|
|
68
118
|
|
|
69
|
-
// Start server
|
|
70
119
|
await server.start();
|
|
71
|
-
console.log('๐ Server running at http://localhost:3000');
|
|
72
120
|
```
|
|
73
121
|
|
|
74
|
-
|
|
122
|
+
> Managers must be registered **before** Services and Controllers so that `AppHandle` references are already populated at startup.
|
|
75
123
|
|
|
76
|
-
|
|
124
|
+
## Components
|
|
77
125
|
|
|
78
|
-
###
|
|
126
|
+
### Services โ WebSocket event handlers
|
|
79
127
|
|
|
80
|
-
|
|
128
|
+
A Service groups Socket.IO event handlers. Each public method of the class is automatically bound to the Socket.IO event `<method_name>`.
|
|
81
129
|
|
|
82
130
|
```typescript
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
131
|
+
import { BaseService } from 'ioserver';
|
|
132
|
+
import type { Socket } from 'socket.io';
|
|
133
|
+
|
|
134
|
+
class RoomService extends BaseService {
|
|
135
|
+
async join(socket: Socket, data: { room: string }, callback?: Function) {
|
|
136
|
+
socket.join(data.room);
|
|
137
|
+
socket.to(data.room).emit('user_joined', { id: socket.id });
|
|
138
|
+
if (callback) callback({ joined: data.room });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async leave(socket: Socket, data: { room: string }) {
|
|
142
|
+
socket.leave(data.room);
|
|
88
143
|
}
|
|
89
144
|
}
|
|
145
|
+
|
|
146
|
+
server.addService({ name: 'room', service: RoomService });
|
|
90
147
|
```
|
|
91
148
|
|
|
92
|
-
|
|
149
|
+
Registration options:
|
|
150
|
+
|
|
151
|
+
| Option | Type | Description |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `name` | `string` | Namespace name (used as Socket.IO namespace `/name`) |
|
|
154
|
+
| `service` | `typeof BaseService` | Service class (not an instance) |
|
|
155
|
+
| `middlewares` | `BaseMiddleware[]` | Optional middleware chain for this namespace |
|
|
93
156
|
|
|
94
|
-
|
|
157
|
+
### Controllers โ HTTP route handlers
|
|
158
|
+
|
|
159
|
+
A Controller groups Fastify route handlers. Routes are mapped through a JSON file located in the `routes/` directory (or the path set in `options.routes`).
|
|
95
160
|
|
|
96
161
|
```typescript
|
|
162
|
+
import { BaseController } from 'ioserver';
|
|
163
|
+
import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
164
|
+
|
|
97
165
|
class UserController extends BaseController {
|
|
98
|
-
async getUser(request:
|
|
99
|
-
|
|
100
|
-
|
|
166
|
+
async getUser(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) {
|
|
167
|
+
reply.send({ id: request.params.id });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async createUser(request: FastifyRequest<{ Body: { name: string } }>, reply: FastifyReply) {
|
|
171
|
+
reply.code(201).send({ id: crypto.randomUUID(), name: request.body.name });
|
|
101
172
|
}
|
|
102
173
|
}
|
|
174
|
+
|
|
175
|
+
server.addController({ name: 'user', controller: UserController });
|
|
103
176
|
```
|
|
104
177
|
|
|
105
|
-
|
|
178
|
+
Corresponding route file `routes/user.json`:
|
|
106
179
|
|
|
107
|
-
|
|
180
|
+
```json
|
|
181
|
+
[
|
|
182
|
+
{ "method": "GET", "url": "/users/:id", "handler": "getUser" },
|
|
183
|
+
{ "method": "POST", "url": "/users", "handler": "createUser" }
|
|
184
|
+
]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Registration options:
|
|
188
|
+
|
|
189
|
+
| Option | Type | Description |
|
|
190
|
+
|---|---|---|
|
|
191
|
+
| `name` | `string` | Must match the JSON route file basename (`routes/<name>.json`) |
|
|
192
|
+
| `controller` | `typeof BaseController` | Controller class (not an instance) |
|
|
193
|
+
| `middlewares` | `BaseMiddleware[]` | Optional middleware chain for all routes of this controller |
|
|
194
|
+
|
|
195
|
+
### Managers โ Injectable singletons
|
|
196
|
+
|
|
197
|
+
Managers hold shared state and business logic. They are instantiated once and exposed to every Service, Controller, and Watcher through `this.app`.
|
|
108
198
|
|
|
109
199
|
```typescript
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
200
|
+
import { BaseManager } from 'ioserver';
|
|
201
|
+
|
|
202
|
+
class CacheManager extends BaseManager {
|
|
203
|
+
private store = new Map<string, unknown>();
|
|
204
|
+
|
|
205
|
+
set(key: string, value: unknown) { this.store.set(key, value); }
|
|
206
|
+
get(key: string) { return this.store.get(key); }
|
|
207
|
+
has(key: string) { return this.store.has(key); }
|
|
115
208
|
}
|
|
209
|
+
|
|
210
|
+
server.addManager({ name: 'cache', manager: CacheManager });
|
|
211
|
+
|
|
212
|
+
// In any other component:
|
|
213
|
+
const cache = this.app.getManager('cache') as CacheManager;
|
|
214
|
+
cache.set('session:42', { userId: 42 });
|
|
116
215
|
```
|
|
117
216
|
|
|
118
|
-
|
|
217
|
+
The optional `start()` method is called automatically by the framework after all components are registered and before the server begins accepting connections.
|
|
218
|
+
|
|
219
|
+
### Watchers โ Background tasks
|
|
119
220
|
|
|
120
|
-
|
|
221
|
+
Watchers run independent background loops. Both `watch()` and `stop()` must be implemented.
|
|
121
222
|
|
|
122
223
|
```typescript
|
|
123
|
-
|
|
224
|
+
import { BaseWatcher } from 'ioserver';
|
|
225
|
+
|
|
226
|
+
class CleanupWatcher extends BaseWatcher {
|
|
227
|
+
private timer: ReturnType<typeof setInterval> | null = null;
|
|
228
|
+
|
|
124
229
|
async watch() {
|
|
125
|
-
setInterval(() => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
},
|
|
230
|
+
this.timer = setInterval(async () => {
|
|
231
|
+
const cache = this.app.getManager('cache') as CacheManager;
|
|
232
|
+
// periodic cleanup logic
|
|
233
|
+
}, 60_000);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
stop() {
|
|
237
|
+
if (this.timer) {
|
|
238
|
+
clearInterval(this.timer);
|
|
239
|
+
this.timer = null;
|
|
240
|
+
}
|
|
129
241
|
}
|
|
130
242
|
}
|
|
243
|
+
|
|
244
|
+
server.addWatcher({ name: 'cleanup', watcher: CleanupWatcher });
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Middlewares โ Request and connection guards
|
|
248
|
+
|
|
249
|
+
Middlewares intercept HTTP requests (Fastify `preHandler`) and Socket.IO connections before they reach Controllers or Services.
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { BaseMiddleware } from 'ioserver';
|
|
253
|
+
import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
254
|
+
import type { Socket } from 'socket.io';
|
|
255
|
+
|
|
256
|
+
class AuthMiddleware extends BaseMiddleware {
|
|
257
|
+
// HTTP guard
|
|
258
|
+
async handle(request: FastifyRequest, reply: FastifyReply, next: Function) {
|
|
259
|
+
const token = request.headers.authorization?.split(' ')[1];
|
|
260
|
+
if (!token || !this.verify(token)) {
|
|
261
|
+
return reply.code(401).send({ error: 'Unauthorized' });
|
|
262
|
+
}
|
|
263
|
+
next();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// WebSocket guard
|
|
267
|
+
async handleSocket(socket: Socket, next: Function) {
|
|
268
|
+
const token = socket.handshake.auth?.token;
|
|
269
|
+
if (!token || !this.verify(token)) {
|
|
270
|
+
return next(new Error('Unauthorized'));
|
|
271
|
+
}
|
|
272
|
+
next();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private verify(token: string) { /* JWT verification */ return true; }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Apply to a specific controller or service
|
|
279
|
+
server.addController({ name: 'admin', controller: AdminController, middlewares: [AuthMiddleware] });
|
|
131
280
|
```
|
|
132
281
|
|
|
133
|
-
##
|
|
282
|
+
## Configuration
|
|
134
283
|
|
|
135
|
-
|
|
284
|
+
```typescript
|
|
285
|
+
const server = new IOServer(options);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
| Option | Type | Default | Description |
|
|
289
|
+
|---|---|---|---|
|
|
290
|
+
| `host` | `string` | `'localhost'` | Bind address |
|
|
291
|
+
| `port` | `number` | `8080` | Listen port |
|
|
292
|
+
| `verbose` | `string` | `'ERROR'` | Log level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
|
|
293
|
+
| `cookie` | `boolean` | `false` | Enable Socket.IO cookies |
|
|
294
|
+
| `mode` | `string \| string[]` | `['websocket','polling']` | Socket.IO transport(s) |
|
|
295
|
+
| `cors` | `object` | `undefined` | Fastify CORS options (applied to HTTP and Socket.IO) |
|
|
296
|
+
| `routes` | `string` | `'./routes'` | Directory containing JSON route files |
|
|
297
|
+
| `rootDir` | `string` | `'.'` | Root directory for static file serving |
|
|
298
|
+
| `spaFallback` | `boolean` | `false` | Serve `index.html` for unmatched routes (SPA mode) |
|
|
299
|
+
|
|
300
|
+
### CORS example
|
|
136
301
|
|
|
137
302
|
```typescript
|
|
138
303
|
const server = new IOServer({
|
|
139
|
-
host: '
|
|
140
|
-
port:
|
|
141
|
-
verbose: 'INFO', // Log level
|
|
142
|
-
routes: './routes', // Route definitions directory
|
|
304
|
+
host: '0.0.0.0',
|
|
305
|
+
port: 8080,
|
|
143
306
|
cors: {
|
|
144
|
-
|
|
145
|
-
origin: ['http://localhost:3000'],
|
|
307
|
+
origin: ['https://app.example.com'],
|
|
146
308
|
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
147
309
|
credentials: true,
|
|
148
310
|
},
|
|
149
|
-
mode: ['websocket', 'polling'], // Socket.IO transport modes
|
|
150
311
|
});
|
|
151
312
|
```
|
|
152
313
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
Define HTTP routes in JSON files (e.g., `routes/api.json`):
|
|
156
|
-
|
|
157
|
-
```json
|
|
158
|
-
[
|
|
159
|
-
{
|
|
160
|
-
"method": "GET",
|
|
161
|
-
"url": "/users/:id",
|
|
162
|
-
"handler": "getUser"
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
"method": "POST",
|
|
166
|
-
"url": "/users",
|
|
167
|
-
"handler": "createUser"
|
|
168
|
-
}
|
|
169
|
-
]
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## ๐งช Testing
|
|
173
|
-
|
|
174
|
-
IOServer includes comprehensive testing utilities and examples:
|
|
314
|
+
## Testing
|
|
175
315
|
|
|
176
316
|
```bash
|
|
177
317
|
# Run all tests
|
|
178
|
-
|
|
318
|
+
pnpm test
|
|
179
319
|
|
|
180
|
-
#
|
|
181
|
-
|
|
182
|
-
npm run test:integration # Integration tests
|
|
183
|
-
npm run test:e2e # End-to-end tests
|
|
184
|
-
npm run test:performance # Performance tests
|
|
320
|
+
# With coverage report
|
|
321
|
+
pnpm run test:coverage
|
|
185
322
|
|
|
186
|
-
#
|
|
187
|
-
|
|
323
|
+
# Isolated suites
|
|
324
|
+
pnpm run test:unit
|
|
325
|
+
pnpm run test:integration
|
|
326
|
+
pnpm run test:e2e
|
|
327
|
+
pnpm run test:performance
|
|
188
328
|
```
|
|
189
329
|
|
|
190
|
-
|
|
330
|
+
Coverage targets: 90% statements, 85% branches, 90% functions.
|
|
191
331
|
|
|
192
|
-
|
|
332
|
+
## Examples
|
|
193
333
|
|
|
194
|
-
|
|
334
|
+
### Simple server
|
|
195
335
|
|
|
196
|
-
|
|
197
|
-
- Real-time messaging
|
|
198
|
-
- Room-based conversations
|
|
199
|
-
- Typing indicators
|
|
200
|
-
- Connection management
|
|
201
|
-
- API endpoints for statistics
|
|
336
|
+
`examples/simple.ts` โ all five component types in a single file, useful as a project template:
|
|
202
337
|
|
|
203
338
|
```bash
|
|
204
|
-
|
|
205
|
-
npm install
|
|
206
|
-
npm start
|
|
339
|
+
pnpm run dev:simple
|
|
207
340
|
```
|
|
208
341
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
## ๐ง API Reference
|
|
342
|
+
### Chat application
|
|
212
343
|
|
|
213
|
-
|
|
344
|
+
`examples/chat-app/` โ a complete multi-room chat server with:
|
|
214
345
|
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
- **`BaseWatcher`** - Base class for background watchers
|
|
346
|
+
- `RoomService` โ join/leave/message Socket.IO events
|
|
347
|
+
- `ChatController` โ REST endpoints for room history and statistics
|
|
348
|
+
- `StatsManager` โ shared counters accessible from both layers
|
|
349
|
+
- `ChatWatcher` โ periodic inactive-room cleanup
|
|
220
350
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
server.addService(options: ServiceOptions)
|
|
226
|
-
server.addController(options: ControllerOptions)
|
|
227
|
-
server.addManager(options: ManagerOptions)
|
|
228
|
-
server.addWatcher(options: WatcherOptions)
|
|
229
|
-
server.start(): Promise<void>
|
|
230
|
-
server.stop(): Promise<void>
|
|
231
|
-
|
|
232
|
-
// Real-time messaging
|
|
233
|
-
server.sendTo(options: SendToOptions): boolean
|
|
351
|
+
```bash
|
|
352
|
+
pnpm run dev:chat
|
|
353
|
+
# or
|
|
354
|
+
cd examples/chat-app && ts-node app.ts
|
|
234
355
|
```
|
|
235
356
|
|
|
236
|
-
|
|
357
|
+
Connect at `http://localhost:8080`.
|
|
237
358
|
|
|
238
|
-
|
|
359
|
+
## Project Organization
|
|
239
360
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
361
|
+
```
|
|
362
|
+
ioserver/
|
|
363
|
+
โโโ src/
|
|
364
|
+
โ โโโ IOServer.ts # Main class โ startup, registration, routing
|
|
365
|
+
โ โโโ BaseClasses.ts # BaseService, BaseController, BaseManager,
|
|
366
|
+
โ โ # BaseWatcher, BaseMiddleware
|
|
367
|
+
โ โโโ IOServerError.ts # Error hierarchy
|
|
368
|
+
โ โโโ index.ts # Public exports
|
|
369
|
+
โโโ examples/
|
|
370
|
+
โ โโโ simple.ts # Minimal example (all component types)
|
|
371
|
+
โ โโโ chat-app/ # Full chat application
|
|
372
|
+
โโโ tests/
|
|
373
|
+
โ โโโ unit/
|
|
374
|
+
โ โโโ integration/
|
|
375
|
+
โ โโโ e2e/
|
|
376
|
+
โ โโโ performance/
|
|
377
|
+
โโโ docs-site/ # Nuxt/Docus documentation site
|
|
378
|
+
โโโ tsconfig.json
|
|
379
|
+
โโโ package.json
|
|
380
|
+
```
|
|
245
381
|
|
|
246
|
-
##
|
|
382
|
+
## Docker Deployment
|
|
383
|
+
|
|
384
|
+
```dockerfile
|
|
385
|
+
FROM node:24-alpine AS builder
|
|
386
|
+
WORKDIR /app
|
|
387
|
+
COPY package*.json ./
|
|
388
|
+
RUN npm ci
|
|
389
|
+
COPY . .
|
|
390
|
+
RUN npm run build
|
|
391
|
+
|
|
392
|
+
FROM node:24-alpine
|
|
393
|
+
WORKDIR /app
|
|
394
|
+
COPY --from=builder /app/dist ./dist
|
|
395
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
396
|
+
COPY --from=builder /app/package.json .
|
|
397
|
+
EXPOSE 8080
|
|
398
|
+
CMD ["node", "dist/index.js"]
|
|
399
|
+
```
|
|
247
400
|
|
|
248
|
-
|
|
401
|
+
```yaml
|
|
402
|
+
# compose.yml
|
|
403
|
+
services:
|
|
404
|
+
app:
|
|
405
|
+
build: .
|
|
406
|
+
ports:
|
|
407
|
+
- "8080:8080"
|
|
408
|
+
environment:
|
|
409
|
+
NODE_ENV: production
|
|
410
|
+
restart: unless-stopped
|
|
411
|
+
```
|
|
249
412
|
|
|
250
|
-
##
|
|
413
|
+
## Related Projects
|
|
251
414
|
|
|
252
|
-
-
|
|
253
|
-
-
|
|
254
|
-
- Inspired by modern microservice architectures
|
|
415
|
+
- [uPKI CA Server](https://github.com/circle-rd/upki-ca) โ Certificate Authority built on this framework pattern
|
|
416
|
+
- [uPKI RA Server](https://github.com/circle-rd/upki-ra) โ Registration Authority built on this framework pattern
|
|
255
417
|
|
|
256
|
-
##
|
|
418
|
+
## Contributing
|
|
257
419
|
|
|
258
|
-
|
|
259
|
-
- ๐ [Issue Tracker](https://github.com/x42en/IOServer/issues)
|
|
260
|
-
- ๐ฌ [Discussions](https://github.com/x42en/IOServer/discussions)
|
|
420
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development setup, component rules, test strategy, and commit conventions.
|
|
261
421
|
|
|
262
|
-
|
|
422
|
+
## License
|
|
263
423
|
|
|
264
|
-
|
|
265
|
-
<strong>Built with โค๏ธ for the Node.js community</strong>
|
|
266
|
-
</div>
|
|
424
|
+
Apache-2.0 โ see [LICENSE](LICENSE).
|
package/dist/BaseClasses.d.ts
CHANGED
package/dist/BaseClasses.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* for Services, Controllers, Managers, Watchers, and Middlewares in the IOServer framework.
|
|
7
7
|
*
|
|
8
8
|
* @author Ben Mz <0x42en@users.noreply.github.com>
|
|
9
|
-
* @version 2.
|
|
9
|
+
* @version 2.1.1
|
|
10
10
|
* @since 1.0.0
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
package/dist/IOServer.d.ts
CHANGED
package/dist/IOServer.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Services, Controllers, Managers, and Watchers.
|
|
6
6
|
*
|
|
7
7
|
* @author Ben Mz <0x42en@users.noreply.github.com>
|
|
8
|
-
* @version 2.
|
|
8
|
+
* @version 2.1.1
|
|
9
9
|
* @since 1.0.0
|
|
10
10
|
*/
|
|
11
11
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -697,7 +697,7 @@ class IOServer {
|
|
|
697
697
|
}
|
|
698
698
|
}
|
|
699
699
|
exports.IOServer = IOServer;
|
|
700
|
-
IOServer.VERSION = '2.1.
|
|
700
|
+
IOServer.VERSION = '2.1.1';
|
|
701
701
|
IOServer.DEFAULT_PORT = 8080;
|
|
702
702
|
IOServer.DEFAULT_HOST = 'localhost';
|
|
703
703
|
IOServer.LOG_LEVELS = [
|
package/dist/IOServerError.d.ts
CHANGED
package/dist/IOServerError.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ioserver",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Damn simple Fastify & Socket.io server framework with TypeScript support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"build:watch": "tsc --watch",
|
|
10
|
+
"dev": "ts-node",
|
|
11
|
+
"dev:chat": "ts-node examples/chat-app/app.ts",
|
|
12
|
+
"dev:simple": "ts-node examples/simple.ts",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"test:unit": "jest --testPathPattern=unit",
|
|
17
|
+
"test:integration": "jest --testPathPattern=integration",
|
|
18
|
+
"test:e2e": "jest --testPathPattern=e2e",
|
|
19
|
+
"test:performance": "jest --testPathPattern=performance",
|
|
20
|
+
"lint": "eslint .",
|
|
21
|
+
"lint:fix": "eslint . --fix",
|
|
22
|
+
"docs:build": "cd docs && make html",
|
|
23
|
+
"docs:api": "typedoc --options typedoc.json",
|
|
24
|
+
"docs:serve": "cd docs/build/html && python -m http.server 8000",
|
|
25
|
+
"gpr-setup": "node scripts/gpr.js",
|
|
26
|
+
"gpr-restore": "node scripts/gpr-restore.js",
|
|
27
|
+
"prepublishOnly": "npm run build && npm run test"
|
|
28
|
+
},
|
|
7
29
|
"keywords": [
|
|
8
30
|
"socket.io",
|
|
9
31
|
"fastify",
|
|
@@ -33,49 +55,28 @@
|
|
|
33
55
|
},
|
|
34
56
|
"dependencies": {
|
|
35
57
|
"@fastify/cors": "^10.1.0",
|
|
36
|
-
"@fastify/sensible": "^6.0.
|
|
58
|
+
"@fastify/sensible": "^6.0.4",
|
|
37
59
|
"@fastify/static": "^9.0.0",
|
|
38
|
-
"fastify": "^5.
|
|
39
|
-
"socket.io": "^4.8.
|
|
60
|
+
"fastify": "^5.7.4",
|
|
61
|
+
"socket.io": "^4.8.3"
|
|
40
62
|
},
|
|
41
63
|
"devDependencies": {
|
|
42
|
-
"@eslint/js": "^9.
|
|
64
|
+
"@eslint/js": "^9.39.3",
|
|
43
65
|
"@types/jest": "^29.5.14",
|
|
44
|
-
"@types/node": "^22.
|
|
66
|
+
"@types/node": "^22.19.12",
|
|
45
67
|
"@types/supertest": "^6.0.3",
|
|
46
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
47
|
-
"@typescript-eslint/parser": "^8.
|
|
48
|
-
"eslint": "^9.
|
|
68
|
+
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
69
|
+
"@typescript-eslint/parser": "^8.56.1",
|
|
70
|
+
"eslint": "^9.39.3",
|
|
49
71
|
"jest": "^29.7.0",
|
|
50
|
-
"socket.io-client": "^4.8.
|
|
51
|
-
"supertest": "^7.
|
|
52
|
-
"ts-jest": "^29.
|
|
72
|
+
"socket.io-client": "^4.8.3",
|
|
73
|
+
"supertest": "^7.2.2",
|
|
74
|
+
"ts-jest": "^29.4.6",
|
|
53
75
|
"ts-node": "^10.9.2",
|
|
54
|
-
"typedoc": "^0.28.
|
|
55
|
-
"typescript": "^5.
|
|
76
|
+
"typedoc": "^0.28.17",
|
|
77
|
+
"typescript": "^5.9.3"
|
|
56
78
|
},
|
|
57
79
|
"peerDependencies": {
|
|
58
80
|
"typescript": ">=5.0.0"
|
|
59
|
-
},
|
|
60
|
-
"scripts": {
|
|
61
|
-
"build": "tsc",
|
|
62
|
-
"build:watch": "tsc --watch",
|
|
63
|
-
"dev": "ts-node",
|
|
64
|
-
"dev:chat": "ts-node examples/chat-app/app.ts",
|
|
65
|
-
"dev:simple": "ts-node examples/simple-example.ts",
|
|
66
|
-
"test": "jest",
|
|
67
|
-
"test:watch": "jest --watch",
|
|
68
|
-
"test:coverage": "jest --coverage",
|
|
69
|
-
"test:unit": "jest --testPathPattern=unit",
|
|
70
|
-
"test:integration": "jest --testPathPattern=integration",
|
|
71
|
-
"test:e2e": "jest --testPathPattern=e2e",
|
|
72
|
-
"test:performance": "jest --testPathPattern=performance",
|
|
73
|
-
"lint": "eslint .",
|
|
74
|
-
"lint:fix": "eslint . --fix",
|
|
75
|
-
"docs:build": "cd docs && make html",
|
|
76
|
-
"docs:api": "typedoc --options typedoc.json",
|
|
77
|
-
"docs:serve": "cd docs/build/html && python -m http.server 8000",
|
|
78
|
-
"gpr-setup": "node scripts/gpr.js",
|
|
79
|
-
"gpr-restore": "node scripts/gpr-restore.js"
|
|
80
81
|
}
|
|
81
|
-
}
|
|
82
|
+
}
|