vibe-gx 1.0.6 โ 2.0.0
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 +422 -93
- package/binding.gyp +36 -0
- package/native/json_stringify.cc +241 -0
- package/native/json_stringify.h +32 -0
- package/native/url_parser.cc +178 -0
- package/native/url_parser.h +34 -0
- package/native/vibe_native.cc +46 -0
- package/package.json +21 -11
- package/utils/core/parser.js +23 -9
- package/utils/core/response.js +8 -5
- package/utils/core/server.js +4 -17
- package/utils/helpers/adapt.js +113 -29
- package/utils/native.js +169 -0
- package/vibe.d.ts +150 -0
package/README.md
CHANGED
|
@@ -2,159 +2,297 @@
|
|
|
2
2
|
<img src="./assets/vlogo.png" alt="Vibe Logo" width="180" />
|
|
3
3
|
<h1>Vibe</h1>
|
|
4
4
|
<p>
|
|
5
|
-
<b>
|
|
5
|
+
<b>The fastest Node.js web framework with the simplest syntax.</b>
|
|
6
|
+
</p>
|
|
7
|
+
<p>
|
|
8
|
+
<img src="https://img.shields.io/badge/performance-14,687_RPS-brightgreen" alt="Performance" />
|
|
9
|
+
<img src="https://img.shields.io/badge/vs_Express-2.2x_faster-blue" alt="vs Express" />
|
|
10
|
+
<img src="https://img.shields.io/badge/vs_Fastify-30%25_faster-orange" alt="vs Fastify" />
|
|
11
|
+
<img src="https://img.shields.io/badge/license-MIT-green" alt="License" />
|
|
6
12
|
</p>
|
|
7
13
|
</div>
|
|
8
14
|
|
|
9
15
|
---
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
## ๐ฆ Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install vibe-gx
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Optional: Build C++ Native Module
|
|
24
|
+
|
|
25
|
+
For maximum performance, build the optional C++ native module:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm run build:native
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
> If the build fails, Vibe automatically falls back to pure JavaScript with zero configuration.
|
|
32
|
+
|
|
33
|
+
---
|
|
12
34
|
|
|
13
|
-
|
|
35
|
+
## ๐ Why Vibe?
|
|
36
|
+
|
|
37
|
+
| Metric | Vibe | Express | Fastify |
|
|
38
|
+
| :------------------------ | :------------: | :-------: | :--------: |
|
|
39
|
+
| **JSON Performance** | **14,687 RPS** | 6,629 RPS | 11,289 RPS |
|
|
40
|
+
| **Install Size** | **~280 KB** | ~5 MB | ~4 MB |
|
|
41
|
+
| **Lines for Hello World** | 3 | 5 | 6 |
|
|
42
|
+
| **Dependencies** | 2 | 30+ | 15+ |
|
|
43
|
+
| **Built-in Clustering** | โ
| โ | โ |
|
|
44
|
+
| **Built-in Caching** | โ
| โ | โ |
|
|
45
|
+
| **C++ Optimizations** | โ
| โ | โ |
|
|
46
|
+
|
|
47
|
+
> **Vibe is faster than Fastify, simpler than Express, and 14-18x smaller than both.**
|
|
48
|
+
|
|
49
|
+
---
|
|
14
50
|
|
|
15
51
|
## โก Features
|
|
16
52
|
|
|
17
|
-
| Feature | Description
|
|
18
|
-
| :----------------------- |
|
|
19
|
-
| ๐ **
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
53
|
+
| Feature | Description |
|
|
54
|
+
| :----------------------- | :--------------------------------------------------------- |
|
|
55
|
+
| ๐ **C++ Native Module** | JSON stringify & URL parsing in C++ |
|
|
56
|
+
| ๐ฏ **Hybrid Router** | O(1) static + O(log n) Trie routing |
|
|
57
|
+
| ๐ **Plugin System** | Fastify-style `register()` with encapsulation |
|
|
58
|
+
| ๐จ **Decorators** | Extend app, request, and response |
|
|
59
|
+
| โก **Cluster Mode** | Built-in multi-process scaling |
|
|
60
|
+
| ๐พ **LRU Cache** | Built-in response caching with ETag |
|
|
61
|
+
| ๐ **Connection Pool** | Generic pool for databases |
|
|
62
|
+
| ๐ **File Uploads** | Multipart uploads with size/type validation |
|
|
63
|
+
| ๐ **Streaming** | Large file uploads without buffering |
|
|
64
|
+
| ๐ **Security** | Path traversal protection, body limits, error sanitization |
|
|
65
|
+
| ๐ **Express Adapter** | Use any Express middleware with `adapt()` |
|
|
66
|
+
|
|
67
|
+
---
|
|
26
68
|
|
|
27
69
|
## ๐ Quick Start
|
|
28
70
|
|
|
29
71
|
```javascript
|
|
30
|
-
import vibe from "
|
|
72
|
+
import vibe from "vibe-gx";
|
|
31
73
|
|
|
32
74
|
const app = vibe();
|
|
33
75
|
|
|
76
|
+
// Direct value - no callback needed!
|
|
34
77
|
app.get("/", "Hello Vibe!");
|
|
35
|
-
|
|
78
|
+
|
|
79
|
+
// Auto JSON response - just return an object
|
|
80
|
+
app.get("/users/:id", (req) => ({ userId: req.params.id }));
|
|
36
81
|
|
|
37
82
|
app.listen(3000);
|
|
38
83
|
```
|
|
39
84
|
|
|
85
|
+
**That's it.** No `res.send()`, no `res.json()` - just return data.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
40
89
|
## ๐ Core API
|
|
41
90
|
|
|
42
91
|
### Routes
|
|
43
92
|
|
|
93
|
+
Vibe supports all standard HTTP methods with a clean, flexible syntax:
|
|
94
|
+
|
|
44
95
|
```javascript
|
|
45
|
-
|
|
46
|
-
app.
|
|
47
|
-
|
|
96
|
+
// String response
|
|
97
|
+
app.get("/", "Hello World");
|
|
98
|
+
|
|
99
|
+
// JSON response (just return an object)
|
|
100
|
+
app.get("/json", { message: "Hello" });
|
|
101
|
+
|
|
102
|
+
// Handler function with request access
|
|
103
|
+
app.get("/users/:id", (req) => ({ id: req.params.id }));
|
|
104
|
+
|
|
105
|
+
// Multiple route parameters
|
|
106
|
+
app.get("/posts/:postId/comments/:commentId", (req) => ({
|
|
107
|
+
postId: req.params.postId,
|
|
108
|
+
commentId: req.params.commentId,
|
|
109
|
+
}));
|
|
110
|
+
|
|
111
|
+
// With options (interceptors, file uploads)
|
|
112
|
+
app.post("/protected", { intercept: authCheck }, handler);
|
|
113
|
+
|
|
114
|
+
// All HTTP methods
|
|
115
|
+
app.get("/");
|
|
116
|
+
app.post("/");
|
|
117
|
+
app.put("/");
|
|
118
|
+
app.del("/"); // DELETE
|
|
119
|
+
app.patch("/");
|
|
120
|
+
app.head("/");
|
|
48
121
|
```
|
|
49
122
|
|
|
50
|
-
###
|
|
123
|
+
### Query Parameters
|
|
51
124
|
|
|
52
125
|
```javascript
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
);
|
|
126
|
+
// GET /search?q=hello&page=2
|
|
127
|
+
app.get("/search", (req) => ({
|
|
128
|
+
query: req.query.q, // "hello"
|
|
129
|
+
page: req.query.page, // "2"
|
|
130
|
+
}));
|
|
59
131
|
```
|
|
60
132
|
|
|
61
|
-
###
|
|
133
|
+
### Request Body
|
|
62
134
|
|
|
63
135
|
```javascript
|
|
64
|
-
app.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
this.success(d);
|
|
136
|
+
app.post("/users", (req) => {
|
|
137
|
+
const { name, email } = req.body;
|
|
138
|
+
return { created: { name, email } };
|
|
68
139
|
});
|
|
69
140
|
```
|
|
70
141
|
|
|
71
142
|
---
|
|
72
143
|
|
|
73
|
-
##
|
|
144
|
+
## ๐ Plugins (Fastify-style)
|
|
74
145
|
|
|
75
|
-
|
|
146
|
+
Plugins provide encapsulated route groups with optional prefixes:
|
|
76
147
|
|
|
77
148
|
```javascript
|
|
78
|
-
|
|
149
|
+
// Register a plugin with prefix
|
|
150
|
+
await app.register(
|
|
151
|
+
async (api) => {
|
|
152
|
+
api.get("/status", { status: "ok" }); // GET /api/status
|
|
153
|
+
api.get("/health", { healthy: true }); // GET /api/health
|
|
154
|
+
|
|
155
|
+
// Plugins can have their own interceptors
|
|
156
|
+
api.plugin((req, res) => {
|
|
157
|
+
console.log(`[API] ${req.method} ${req.url}`);
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
{ prefix: "/api" },
|
|
161
|
+
);
|
|
79
162
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
app.listen(3000);
|
|
163
|
+
// Nested plugins
|
|
164
|
+
await app.register(
|
|
165
|
+
async (v1) => {
|
|
166
|
+
v1.get("/users", { version: 1 }); // GET /api/v1/users
|
|
85
167
|
},
|
|
86
|
-
{
|
|
168
|
+
{ prefix: "/api/v1" },
|
|
87
169
|
);
|
|
88
170
|
```
|
|
89
171
|
|
|
90
|
-
|
|
172
|
+
---
|
|
91
173
|
|
|
92
|
-
|
|
93
|
-
import vibe, { LRUCache, cacheMiddleware } from "./vibe.js";
|
|
174
|
+
## ๐ก๏ธ Interceptors (Middleware)
|
|
94
175
|
|
|
95
|
-
|
|
96
|
-
|
|
176
|
+
Interceptors run before your handler. Return `false` to stop execution.
|
|
177
|
+
|
|
178
|
+
### Single Interceptor
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
const authCheck = (req, res) => {
|
|
182
|
+
if (!req.headers.authorization) {
|
|
183
|
+
res.unauthorized("Token required");
|
|
184
|
+
return false; // Stop execution
|
|
185
|
+
}
|
|
186
|
+
req.user = { id: 1 };
|
|
187
|
+
return true; // Continue to handler
|
|
188
|
+
};
|
|
97
189
|
|
|
98
|
-
app.get("/
|
|
99
|
-
return {
|
|
190
|
+
app.get("/protected", { intercept: authCheck }, (req) => {
|
|
191
|
+
return { user: req.user };
|
|
100
192
|
});
|
|
101
193
|
```
|
|
102
194
|
|
|
103
|
-
###
|
|
195
|
+
### Multiple Interceptors
|
|
104
196
|
|
|
105
197
|
```javascript
|
|
106
|
-
|
|
198
|
+
app.get(
|
|
199
|
+
"/admin",
|
|
200
|
+
{
|
|
201
|
+
intercept: [authCheck, adminCheck, rateLimiter],
|
|
202
|
+
},
|
|
203
|
+
handler,
|
|
204
|
+
);
|
|
205
|
+
```
|
|
107
206
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
207
|
+
### Global Interceptors
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
// Applies to ALL routes
|
|
211
|
+
app.plugin((req, res) => {
|
|
212
|
+
console.log(`${req.method} ${req.url}`);
|
|
112
213
|
});
|
|
214
|
+
```
|
|
113
215
|
|
|
114
|
-
|
|
216
|
+
---
|
|
115
217
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
218
|
+
## ๐จ Decorators
|
|
219
|
+
|
|
220
|
+
Extend app, request, or response with custom properties:
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
// App decorator - shared config
|
|
224
|
+
app.decorate("config", { env: "production", version: "1.0.0" });
|
|
225
|
+
|
|
226
|
+
// Access via app.decorators.config
|
|
227
|
+
app.get("/version", () => ({ version: app.decorators.config.version }));
|
|
228
|
+
|
|
229
|
+
// Request decorator - add to all requests
|
|
230
|
+
app.decorateRequest("timestamp", () => Date.now());
|
|
231
|
+
|
|
232
|
+
app.get("/time", (req) => ({ timestamp: req.timestamp }));
|
|
233
|
+
|
|
234
|
+
// Reply decorator - add methods to response
|
|
235
|
+
app.decorateReply("sendSuccess", function (data) {
|
|
236
|
+
this.success(data);
|
|
120
237
|
});
|
|
121
238
|
```
|
|
122
239
|
|
|
123
|
-
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## ๐ File Uploads
|
|
124
243
|
|
|
125
244
|
Vibe supports multipart file uploads with built-in validation.
|
|
126
245
|
|
|
127
|
-
|
|
246
|
+
### Basic Upload
|
|
128
247
|
|
|
129
248
|
```javascript
|
|
130
|
-
app.post("/upload", { media: { dest: "uploads" } }, (req
|
|
249
|
+
app.post("/upload", { media: { dest: "uploads" } }, (req) => {
|
|
250
|
+
>>>>>>> cpp-optimization
|
|
131
251
|
return { files: req.files, body: req.body };
|
|
132
252
|
});
|
|
133
253
|
```
|
|
134
254
|
|
|
135
|
-
|
|
255
|
+
### Media Options
|
|
256
|
+
>>>>>>> cpp-optimization
|
|
136
257
|
|
|
137
258
|
```javascript
|
|
138
259
|
app.post(
|
|
139
260
|
"/upload",
|
|
140
261
|
{
|
|
141
262
|
media: {
|
|
142
|
-
dest: "uploads", //
|
|
263
|
+
dest: "uploads", // Subfolder destination
|
|
143
264
|
public: true, // Save inside public folder (default: true)
|
|
144
265
|
maxSize: 5 * 1024 * 1024, // Max file size: 5MB
|
|
145
|
-
allowedTypes: ["image/jpeg", "image/png", "
|
|
266
|
+
allowedTypes: ["image/jpeg", "image/png", "image/*"], // Wildcards supported
|
|
146
267
|
},
|
|
147
268
|
},
|
|
148
269
|
handler,
|
|
149
270
|
);
|
|
150
271
|
```
|
|
151
272
|
|
|
152
|
-
|
|
273
|
+
### Uploaded File Object
|
|
274
|
+
|
|
275
|
+
```javascript
|
|
276
|
+
// req.files contains:
|
|
277
|
+
[
|
|
278
|
+
{
|
|
279
|
+
filename: "image-a7x92b.png", // Saved filename (safe)
|
|
280
|
+
originalName: "photo.png", // Original filename
|
|
281
|
+
type: "image/png", // MIME type
|
|
282
|
+
filePath: "/uploads/image-a7x92b.png", // Full path
|
|
283
|
+
size: 102400, // Size in bytes
|
|
284
|
+
},
|
|
285
|
+
];
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Streaming Uploads (Large Files)
|
|
289
|
+
|
|
290
|
+
For large files, use streaming mode to avoid buffering in memory:
|
|
153
291
|
|
|
154
292
|
```javascript
|
|
155
293
|
import fs from "fs";
|
|
156
294
|
|
|
157
|
-
app.post("/upload", { media: { streaming: true } }, (req
|
|
295
|
+
app.post("/upload-large", { media: { streaming: true } }, (req) => {
|
|
158
296
|
req.on("file", (name, stream, info) => {
|
|
159
297
|
stream.pipe(fs.createWriteStream(`/uploads/${info.filename}`));
|
|
160
298
|
});
|
|
@@ -162,21 +300,126 @@ app.post("/upload", { media: { streaming: true } }, (req, res) => {
|
|
|
162
300
|
});
|
|
163
301
|
```
|
|
164
302
|
|
|
165
|
-
|
|
303
|
+
### Error Handling
|
|
304
|
+
|
|
305
|
+
- **413 Payload Too Large** - File exceeds `maxSize`
|
|
306
|
+
- **415 Unsupported Media Type** - File type not in `allowedTypes`
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## ๐ฅ Scalability
|
|
311
|
+
|
|
312
|
+
### Cluster Mode
|
|
313
|
+
|
|
314
|
+
Scale across all CPU cores automatically:
|
|
166
315
|
|
|
167
316
|
```javascript
|
|
168
|
-
|
|
169
|
-
|
|
317
|
+
import vibe, { clusterize, isPrimary, getWorkerId } from "vibe-gx";
|
|
318
|
+
|
|
319
|
+
clusterize(
|
|
320
|
+
() => {
|
|
321
|
+
const app = vibe();
|
|
322
|
+
app.get("/", `Hello from worker ${getWorkerId()}!`);
|
|
323
|
+
app.listen(3000);
|
|
324
|
+
},
|
|
170
325
|
{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
filePath: "/uploads/image.png", // Full path
|
|
175
|
-
size: 102400, // Size in bytes
|
|
326
|
+
workers: 4, // Number of workers (default: CPU count)
|
|
327
|
+
restart: true, // Auto-restart crashed workers
|
|
328
|
+
restartDelay: 1000, // Delay before restart (ms)
|
|
176
329
|
},
|
|
177
|
-
|
|
330
|
+
);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### LRU Cache
|
|
334
|
+
|
|
335
|
+
Built-in response caching with ETag support:
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
import vibe, { LRUCache, cacheMiddleware } from "vibe-gx";
|
|
339
|
+
|
|
340
|
+
const cache = new LRUCache({
|
|
341
|
+
max: 1000, // Maximum entries
|
|
342
|
+
ttl: 60000, // TTL in milliseconds (60 seconds)
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
app.get("/expensive", { intercept: cacheMiddleware(cache) }, async () => {
|
|
346
|
+
// This only runs on cache MISS
|
|
347
|
+
return await expensiveOperation();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Manual cache operations
|
|
351
|
+
cache.set("key", { data: "value" });
|
|
352
|
+
cache.get("key"); // { value, expires, etag }
|
|
353
|
+
cache.delete("key");
|
|
354
|
+
cache.clear();
|
|
178
355
|
```
|
|
179
356
|
|
|
357
|
+
### Connection Pool
|
|
358
|
+
|
|
359
|
+
Generic connection pool for databases:
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
import vibe, { createPool } from "vibe-gx";
|
|
363
|
+
|
|
364
|
+
const dbPool = createPool({
|
|
365
|
+
create: async () => await connectToDatabase(),
|
|
366
|
+
destroy: async (conn) => await conn.close(),
|
|
367
|
+
validate: (conn) => conn.isAlive(),
|
|
368
|
+
min: 2, // Minimum connections
|
|
369
|
+
max: 10, // Maximum connections
|
|
370
|
+
acquireTimeout: 30000, // Timeout to acquire (ms)
|
|
371
|
+
idleTimeout: 60000, // Idle timeout (ms)
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
app.get("/users", async () => {
|
|
375
|
+
return await dbPool.use(async (conn) => {
|
|
376
|
+
return await conn.query("SELECT * FROM users");
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Pool statistics
|
|
381
|
+
console.log(dbPool.stats);
|
|
382
|
+
// { available: 5, inUse: 2, waiting: 0, max: 10 }
|
|
383
|
+
|
|
384
|
+
// Cleanup on shutdown
|
|
385
|
+
process.on("SIGTERM", () => dbPool.close());
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## ๐ Express Middleware Adapter
|
|
391
|
+
|
|
392
|
+
Use any Express middleware with the adapter:
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
import { adapt } from "vibe-gx/utils/helpers/adapt.js";
|
|
396
|
+
import cors from "cors";
|
|
397
|
+
import helmet from "helmet";
|
|
398
|
+
import compression from "compression";
|
|
399
|
+
|
|
400
|
+
app.plugin(adapt(cors()));
|
|
401
|
+
app.plugin(adapt(helmet()));
|
|
402
|
+
app.plugin(adapt(compression()));
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## ๐ Security
|
|
408
|
+
|
|
409
|
+
Built-in protections:
|
|
410
|
+
|
|
411
|
+
| Feature | Status |
|
|
412
|
+
| :-------------------------------------- | :----: |
|
|
413
|
+
| Path traversal protection | โ
|
|
|
414
|
+
| File type validation | โ
|
|
|
415
|
+
| Body size limits (1MB JSON, 10MB files) | โ
|
|
|
416
|
+
| Error sanitization (production mode) | โ
|
|
|
417
|
+
| Safe filename generation | โ
|
|
|
418
|
+
| Port validation | โ
|
|
|
419
|
+
|
|
420
|
+
Set `NODE_ENV=production` for secure error handling (stack traces hidden).
|
|
421
|
+
|
|
422
|
+
>>>>>>> cpp-optimization
|
|
180
423
|
---
|
|
181
424
|
|
|
182
425
|
### Interceptors (Middleware)
|
|
@@ -245,34 +488,120 @@ app.post(
|
|
|
245
488
|
|
|
246
489
|
### Application
|
|
247
490
|
|
|
248
|
-
| Method
|
|
249
|
-
|
|
|
250
|
-
| `app.
|
|
251
|
-
| `app.
|
|
252
|
-
| `app.
|
|
253
|
-
| `app.plugin(fn)`
|
|
491
|
+
| Method | Description |
|
|
492
|
+
| :----------------------------------------------- | :------------------- |
|
|
493
|
+
| `app.get/post/put/del/patch/head(path, handler)` | Register route |
|
|
494
|
+
| `app.listen(port, host?, callback?)` | Start server |
|
|
495
|
+
| `app.register(fn, { prefix })` | Register plugin |
|
|
496
|
+
| `app.plugin(fn)` | Global interceptor |
|
|
497
|
+
| `app.decorate(name, value)` | Add app property |
|
|
498
|
+
| `app.decorateRequest(name, value)` | Add to all requests |
|
|
499
|
+
| `app.decorateReply(name, value)` | Add to all responses |
|
|
500
|
+
| `app.setPublicFolder(path)` | Set static folder |
|
|
501
|
+
| `app.logRoutes()` | Log all routes |
|
|
254
502
|
|
|
255
503
|
### Request (`req`)
|
|
256
504
|
|
|
257
|
-
| Property
|
|
258
|
-
|
|
|
259
|
-
| `req.params`
|
|
260
|
-
| `req.query`
|
|
261
|
-
| `req.body`
|
|
262
|
-
| `req.files`
|
|
505
|
+
| Property | Description |
|
|
506
|
+
| :------------ | :------------------------- |
|
|
507
|
+
| `req.params` | Route parameters (`:id`) |
|
|
508
|
+
| `req.query` | Query string (`?page=1`) |
|
|
509
|
+
| `req.body` | Parsed JSON/form body |
|
|
510
|
+
| `req.files` | Uploaded files (multipart) |
|
|
511
|
+
| `req.ip` | Client IP address |
|
|
512
|
+
| `req.method` | HTTP method |
|
|
513
|
+
| `req.url` | Request URL |
|
|
514
|
+
| `req.headers` | Request headers |
|
|
263
515
|
|
|
264
516
|
### Response (`res`)
|
|
265
517
|
|
|
266
|
-
| Method
|
|
267
|
-
|
|
|
268
|
-
| `res.json(data)`
|
|
269
|
-
| `res.send(data)`
|
|
270
|
-
| `res.status(code)`
|
|
271
|
-
| `res.
|
|
272
|
-
| `res.
|
|
518
|
+
| Method | Description |
|
|
519
|
+
| :---------------------------------- | :--------------------------- |
|
|
520
|
+
| `res.json(data)` | Send JSON |
|
|
521
|
+
| `res.send(data)` | Send any response |
|
|
522
|
+
| `res.status(code)` | Set status (chainable) |
|
|
523
|
+
| `res.redirect(url, code?)` | Redirect (302) |
|
|
524
|
+
| `res.sendFile(path)` | Send file from public folder |
|
|
525
|
+
| `res.sendAbsoluteFile(path, opts?)` | Send file from any path |
|
|
526
|
+
| `res.sendHtml(filename)` | Send HTML file |
|
|
527
|
+
| `res.success(data?, msg?)` | 200 OK |
|
|
528
|
+
| `res.created(data?, msg?)` | 201 Created |
|
|
529
|
+
| `res.badRequest(msg?, errors?)` | 400 Bad Request |
|
|
530
|
+
| `res.unauthorized(msg?)` | 401 Unauthorized |
|
|
531
|
+
| `res.forbidden(msg?)` | 403 Forbidden |
|
|
532
|
+
| `res.notFound(msg?)` | 404 Not Found |
|
|
533
|
+
| `res.conflict(msg?)` | 409 Conflict |
|
|
534
|
+
| `res.serverError(err?)` | 500 Server Error |
|
|
535
|
+
|
|
536
|
+
### Cluster Utilities
|
|
537
|
+
|
|
538
|
+
| Function | Description |
|
|
539
|
+
| :--------------------- | :---------------------------- |
|
|
540
|
+
| `clusterize(fn, opts)` | Start in cluster mode |
|
|
541
|
+
| `isPrimary()` | Check if primary process |
|
|
542
|
+
| `isWorker()` | Check if worker process |
|
|
543
|
+
| `getWorkerId()` | Get worker ID (0 for primary) |
|
|
544
|
+
| `getWorkerCount()` | Get number of active workers |
|
|
545
|
+
|
|
546
|
+
### Cache Utilities
|
|
547
|
+
|
|
548
|
+
| Class/Function | Description |
|
|
549
|
+
| :-------------------------- | :------------------------ |
|
|
550
|
+
| `new LRUCache(opts)` | Create LRU cache instance |
|
|
551
|
+
| `cacheMiddleware(cache)` | Create cache interceptor |
|
|
552
|
+
| `LRUCache.key(method, url)` | Generate cache key |
|
|
553
|
+
| `LRUCache.etag(value)` | Generate ETag |
|
|
554
|
+
|
|
555
|
+
### Pool Utilities
|
|
556
|
+
|
|
557
|
+
| Class/Function | Description |
|
|
558
|
+
| :----------------- | :--------------------- |
|
|
559
|
+
| `createPool(opts)` | Create connection pool |
|
|
560
|
+
| `pool.acquire()` | Acquire resource |
|
|
561
|
+
| `pool.release(r)` | Release resource |
|
|
562
|
+
| `pool.use(fn)` | Use with auto-release |
|
|
563
|
+
| `pool.close()` | Close pool |
|
|
564
|
+
| `pool.stats` | Get pool statistics |
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## ๐ Benchmarks
|
|
569
|
+
|
|
570
|
+
Run benchmarks yourself:
|
|
571
|
+
|
|
572
|
+
```bash
|
|
573
|
+
npm run benchmark
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
Tested with 5,000 requests, 50 concurrency:
|
|
577
|
+
|
|
578
|
+
```
|
|
579
|
+
Framework | JSON RPS | vs Express | vs Fastify
|
|
580
|
+
-------------|-------------|------------|------------
|
|
581
|
+
Vibe | 14,687 | 2.2x โ
| 1.3x โ
|
|
582
|
+
Fastify | 11,289 | 1.7x | baseline
|
|
583
|
+
Express | 6,629 | baseline | 0.6x
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## ๐งช Testing
|
|
589
|
+
|
|
590
|
+
```bash
|
|
591
|
+
# Run all tests
|
|
592
|
+
npm test
|
|
593
|
+
|
|
594
|
+
# Run comprehensive tests
|
|
595
|
+
npm run test:all
|
|
596
|
+
|
|
597
|
+
# Run benchmarks
|
|
598
|
+
npm run benchmark
|
|
599
|
+
```
|
|
273
600
|
|
|
274
601
|
---
|
|
275
602
|
|
|
276
603
|
## ๐ License
|
|
277
604
|
|
|
278
|
-
Part of the **GeNeSix** brand. Created by **Nnamdi "Joe" Amaga**.
|
|
605
|
+
Part of the **GeNeSix** brand. Created by **Nnamdi "Joe" Amaga**.
|
|
606
|
+
|
|
607
|
+
MIT License.
|
package/binding.gyp
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "vibe_native",
|
|
5
|
+
"cflags!": ["-fno-exceptions"],
|
|
6
|
+
"cflags_cc!": ["-fno-exceptions"],
|
|
7
|
+
"cflags_cc": ["-std=c++17", "-O3"],
|
|
8
|
+
"sources": [
|
|
9
|
+
"native/vibe_native.cc",
|
|
10
|
+
"native/json_stringify.cc",
|
|
11
|
+
"native/url_parser.cc"
|
|
12
|
+
],
|
|
13
|
+
"include_dirs": [
|
|
14
|
+
"<!@(node -p \"require('node-addon-api').include\")"
|
|
15
|
+
],
|
|
16
|
+
"defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
|
|
17
|
+
"conditions": [
|
|
18
|
+
["OS=='mac'", {
|
|
19
|
+
"xcode_settings": {
|
|
20
|
+
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
|
|
21
|
+
"CLANG_CXX_LANGUAGE_STANDARD": "c++17",
|
|
22
|
+
"MACOSX_DEPLOYMENT_TARGET": "10.15"
|
|
23
|
+
}
|
|
24
|
+
}],
|
|
25
|
+
["OS=='win'", {
|
|
26
|
+
"msvs_settings": {
|
|
27
|
+
"VCCLCompilerTool": {
|
|
28
|
+
"ExceptionHandling": 1,
|
|
29
|
+
"AdditionalOptions": ["/std:c++17"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}]
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|