qhttpx 1.9.2 → 1.9.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/CHANGELOG.md +14 -0
- package/README.md +79 -17
- package/dist/examples/api-server.d.ts +1 -0
- package/dist/examples/api-server.js +77 -0
- package/dist/examples/basic.d.ts +1 -0
- package/dist/examples/basic.js +10 -0
- package/dist/examples/compression.d.ts +1 -0
- package/dist/examples/compression.js +17 -0
- package/dist/examples/cors.d.ts +1 -0
- package/dist/examples/cors.js +19 -0
- package/dist/examples/errors.d.ts +1 -0
- package/dist/examples/errors.js +25 -0
- package/dist/examples/file-upload.d.ts +1 -0
- package/dist/examples/file-upload.js +24 -0
- package/dist/examples/fusion.d.ts +1 -0
- package/dist/examples/fusion.js +21 -0
- package/dist/examples/rate-limiting.d.ts +1 -0
- package/dist/examples/rate-limiting.js +17 -0
- package/dist/examples/validation.d.ts +1 -0
- package/dist/examples/validation.js +23 -0
- package/dist/examples/websockets.d.ts +1 -0
- package/dist/examples/websockets.js +20 -0
- package/dist/package.json +112 -0
- package/dist/src/benchmarks/compare-frameworks.js +119 -0
- package/dist/src/benchmarks/compare.d.ts +1 -0
- package/dist/src/benchmarks/compare.js +288 -0
- package/dist/src/benchmarks/quantam-users.d.ts +1 -0
- package/dist/src/benchmarks/quantam-users.js +56 -0
- package/dist/src/benchmarks/simple-json.d.ts +1 -0
- package/dist/src/benchmarks/simple-json.js +60 -0
- package/dist/src/benchmarks/ultra-mode.d.ts +1 -0
- package/dist/src/benchmarks/ultra-mode.js +94 -0
- package/dist/src/buffer-pool.js +70 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/cli/index.js +222 -0
- package/dist/src/client/index.d.ts +17 -0
- package/dist/src/client/index.js +72 -0
- package/dist/src/config.js +50 -0
- package/dist/src/cookies.js +59 -0
- package/dist/src/core/batch.d.ts +24 -0
- package/dist/src/core/batch.js +97 -0
- package/dist/src/core/body-parser.d.ts +15 -0
- package/dist/src/core/body-parser.js +121 -0
- package/dist/src/core/buffer-pool.d.ts +41 -0
- package/dist/src/core/buffer-pool.js +70 -0
- package/dist/src/core/config.d.ts +7 -0
- package/dist/src/core/config.js +50 -0
- package/dist/src/core/errors.d.ts +34 -0
- package/dist/src/core/errors.js +70 -0
- package/dist/src/core/fusion.d.ts +20 -0
- package/dist/src/core/fusion.js +193 -0
- package/dist/src/core/logger.d.ts +22 -0
- package/dist/src/core/logger.js +49 -0
- package/dist/src/core/metrics.d.ts +48 -0
- package/dist/src/core/metrics.js +117 -0
- package/dist/src/core/native-adapter.d.ts +11 -0
- package/dist/src/core/native-adapter.js +211 -0
- package/dist/src/core/resources.d.ts +9 -0
- package/dist/src/core/resources.js +25 -0
- package/dist/src/core/scheduler.d.ts +34 -0
- package/dist/src/core/scheduler.js +85 -0
- package/dist/src/core/scope.d.ts +26 -0
- package/dist/src/core/scope.js +68 -0
- package/dist/src/core/serializer.d.ts +10 -0
- package/dist/src/core/serializer.js +44 -0
- package/dist/src/core/server.d.ts +138 -0
- package/dist/src/core/server.js +1082 -0
- package/dist/src/core/stream.d.ts +15 -0
- package/dist/src/core/stream.js +71 -0
- package/dist/src/core/tasks.d.ts +29 -0
- package/dist/src/core/tasks.js +87 -0
- package/dist/src/core/types.d.ts +197 -0
- package/dist/src/core/types.js +19 -0
- package/dist/src/core/websocket.d.ts +25 -0
- package/dist/src/core/websocket.js +86 -0
- package/dist/src/core/worker-queue.d.ts +41 -0
- package/dist/src/core/worker-queue.js +73 -0
- package/dist/src/cors.js +66 -0
- package/dist/src/database/adapters/memory.d.ts +21 -0
- package/dist/src/database/adapters/memory.js +90 -0
- package/dist/src/database/adapters/mongo.d.ts +11 -0
- package/dist/src/database/adapters/mongo.js +141 -0
- package/dist/src/database/adapters/postgres.d.ts +10 -0
- package/dist/src/database/adapters/postgres.js +111 -0
- package/dist/src/database/adapters/sqlite.d.ts +10 -0
- package/dist/src/database/adapters/sqlite.js +42 -0
- package/dist/src/database/coalescer.d.ts +14 -0
- package/dist/src/database/coalescer.js +134 -0
- package/dist/src/database/manager.d.ts +35 -0
- package/dist/src/database/manager.js +87 -0
- package/dist/src/database/types.d.ts +20 -0
- package/dist/src/database/types.js +2 -0
- package/dist/src/index.d.ts +50 -0
- package/dist/src/index.js +93 -0
- package/dist/src/logger.js +45 -0
- package/dist/src/metrics.js +111 -0
- package/dist/src/middleware/compression.d.ts +2 -0
- package/dist/src/middleware/compression.js +133 -0
- package/dist/src/middleware/cors.d.ts +2 -0
- package/dist/src/middleware/cors.js +66 -0
- package/dist/src/middleware/presets.d.ts +15 -0
- package/dist/src/middleware/presets.js +52 -0
- package/dist/src/middleware/rate-limit.d.ts +14 -0
- package/dist/src/middleware/rate-limit.js +83 -0
- package/dist/src/middleware/security.d.ts +10 -0
- package/dist/src/middleware/security.js +69 -0
- package/dist/src/middleware/static.d.ts +11 -0
- package/dist/src/middleware/static.js +191 -0
- package/dist/src/native/index.d.ts +32 -0
- package/dist/src/native/index.js +141 -0
- package/dist/src/openapi/generator.d.ts +19 -0
- package/dist/src/openapi/generator.js +149 -0
- package/dist/src/presets.js +33 -0
- package/dist/src/radix-router.js +89 -0
- package/dist/src/radix-tree.js +81 -0
- package/dist/src/resources.js +25 -0
- package/dist/src/router/radix-router.d.ts +18 -0
- package/dist/src/router/radix-router.js +89 -0
- package/dist/src/router/radix-tree.d.ts +18 -0
- package/dist/src/router/radix-tree.js +131 -0
- package/dist/src/router/router.d.ts +34 -0
- package/dist/src/router/router.js +186 -0
- package/dist/src/router.js +138 -0
- package/dist/src/scheduler.js +85 -0
- package/dist/src/security.js +69 -0
- package/dist/src/server.js +685 -0
- package/dist/src/signals.js +31 -0
- package/dist/src/static.js +107 -0
- package/dist/src/stream.js +71 -0
- package/dist/src/tasks.js +87 -0
- package/dist/src/testing/index.d.ts +25 -0
- package/dist/src/testing/index.js +84 -0
- package/dist/src/testing.js +40 -0
- package/dist/src/types.js +19 -0
- package/dist/src/utils/cookies.d.ts +3 -0
- package/dist/src/utils/cookies.js +59 -0
- package/dist/src/utils/logger.d.ts +2 -0
- package/dist/src/utils/logger.js +45 -0
- package/dist/src/utils/signals.d.ts +6 -0
- package/dist/src/utils/signals.js +31 -0
- package/dist/src/utils/sse.d.ts +6 -0
- package/dist/src/utils/sse.js +32 -0
- package/dist/src/utils/testing.js +40 -0
- package/dist/src/validation/index.d.ts +3 -0
- package/dist/src/validation/index.js +19 -0
- package/dist/src/validation/simple.d.ts +5 -0
- package/dist/src/validation/simple.js +102 -0
- package/dist/src/validation/types.d.ts +32 -0
- package/dist/src/validation/types.js +12 -0
- package/dist/src/validation/zod.d.ts +4 -0
- package/dist/src/validation/zod.js +18 -0
- package/dist/src/views/index.d.ts +1 -0
- package/dist/src/views/index.js +17 -0
- package/dist/src/views/types.d.ts +3 -0
- package/dist/src/views/types.js +2 -0
- package/dist/src/worker-queue.js +73 -0
- package/dist/tests/adapters.test.d.ts +1 -0
- package/dist/tests/adapters.test.js +106 -0
- package/dist/tests/batch.test.d.ts +1 -0
- package/dist/tests/batch.test.js +117 -0
- package/dist/tests/body-parser.test.d.ts +1 -0
- package/dist/tests/body-parser.test.js +52 -0
- package/dist/tests/compression-sse.test.d.ts +1 -0
- package/dist/tests/compression-sse.test.js +87 -0
- package/dist/tests/cookies.test.d.ts +1 -0
- package/dist/tests/cookies.test.js +63 -0
- package/dist/tests/cors.test.d.ts +1 -0
- package/dist/tests/cors.test.js +55 -0
- package/dist/tests/database.test.d.ts +1 -0
- package/dist/tests/database.test.js +80 -0
- package/dist/tests/dx.test.d.ts +1 -0
- package/dist/tests/dx.test.js +114 -0
- package/dist/tests/ecosystem.test.d.ts +1 -0
- package/dist/tests/ecosystem.test.js +133 -0
- package/dist/tests/features.test.d.ts +1 -0
- package/dist/tests/features.test.js +47 -0
- package/dist/tests/fusion.test.d.ts +1 -0
- package/dist/tests/fusion.test.js +92 -0
- package/dist/tests/http-basic.test.d.ts +1 -0
- package/dist/tests/http-basic.test.js +124 -0
- package/dist/tests/logger.test.d.ts +1 -0
- package/dist/tests/logger.test.js +33 -0
- package/dist/tests/middleware.test.d.ts +1 -0
- package/dist/tests/middleware.test.js +109 -0
- package/dist/tests/native-adapter.test.d.ts +1 -0
- package/dist/tests/native-adapter.test.js +71 -0
- package/dist/tests/observability.test.d.ts +1 -0
- package/dist/tests/observability.test.js +59 -0
- package/dist/tests/openapi.test.d.ts +1 -0
- package/dist/tests/openapi.test.js +64 -0
- package/dist/tests/plugin.test.d.ts +1 -0
- package/dist/tests/plugin.test.js +65 -0
- package/dist/tests/plugins.test.d.ts +1 -0
- package/dist/tests/plugins.test.js +71 -0
- package/dist/tests/rate-limit.test.d.ts +1 -0
- package/dist/tests/rate-limit.test.js +77 -0
- package/dist/tests/resources.test.d.ts +1 -0
- package/dist/tests/resources.test.js +47 -0
- package/dist/tests/scheduler.test.d.ts +1 -0
- package/dist/tests/scheduler.test.js +46 -0
- package/dist/tests/schema-routes.test.d.ts +1 -0
- package/dist/tests/schema-routes.test.js +77 -0
- package/dist/tests/security.test.d.ts +1 -0
- package/dist/tests/security.test.js +83 -0
- package/dist/tests/server-db.test.d.ts +1 -0
- package/dist/tests/server-db.test.js +72 -0
- package/dist/tests/smoke.test.d.ts +1 -0
- package/dist/tests/smoke.test.js +10 -0
- package/dist/tests/sqlite-fusion.test.d.ts +1 -0
- package/dist/tests/sqlite-fusion.test.js +92 -0
- package/dist/tests/static.test.d.ts +1 -0
- package/dist/tests/static.test.js +102 -0
- package/dist/tests/stream.test.d.ts +1 -0
- package/dist/tests/stream.test.js +44 -0
- package/dist/tests/task-metrics.test.d.ts +1 -0
- package/dist/tests/task-metrics.test.js +53 -0
- package/dist/tests/tasks.test.d.ts +1 -0
- package/dist/tests/tasks.test.js +62 -0
- package/dist/tests/testing.test.d.ts +1 -0
- package/dist/tests/testing.test.js +47 -0
- package/dist/tests/validation.test.d.ts +1 -0
- package/dist/tests/validation.test.js +107 -0
- package/dist/tests/websocket.test.d.ts +1 -0
- package/dist/tests/websocket.test.js +146 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +9 -0
- package/docs/FUSION.md +19 -0
- package/package.json +2 -1
- package/prebuilds/darwin-arm64/qhttpx.node +0 -0
- package/prebuilds/linux-x64/qhttpx.node +0 -0
- package/prebuilds/win32-x64/qhttpx.node +0 -0
- package/scripts/install-native.js +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.9.4] - 2026-01-20
|
|
6
|
+
**"The Silent Performance Update"**
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **Performance**: Disabled logging by default in standard presets to reduce I/O overhead and improve throughput in production environments.
|
|
10
|
+
|
|
11
|
+
## [1.9.3] - 2026-01-20
|
|
12
|
+
**"The Distribution Fix"**
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Distribution**: Removed `dist` from `.gitignore` to allow committing build artifacts if needed, while maintaining strict `!dist` whitelist in `.npmignore` to guarantee inclusion in the published package.
|
|
16
|
+
- **Documentation**: Added a "Side-by-Side" Code Comparison section to `README.md` comparing QHTTPX, Express, and Fastify to showcase Developer Experience (DX).
|
|
17
|
+
- **Metrics**: Exposed Request Fusion metrics (Total Fused, Total Processed) via the `/__qhttpx/metrics` endpoint for better feature visibility.
|
|
18
|
+
|
|
5
19
|
## [1.9.2] - 2026-01-20
|
|
6
20
|
**"The Dependency Polish Update"**
|
|
7
21
|
|
package/README.md
CHANGED
|
@@ -56,6 +56,52 @@ Most Node.js frameworks rely on the event loop blindly. QHTTPX introduces a **Co
|
|
|
56
56
|
| **Static Assets** | **✅ Smart Streaming** | Standard | Standard | Standard |
|
|
57
57
|
| **Type Safety** | **✅ First-class** | Good | Partial | First-class |
|
|
58
58
|
|
|
59
|
+
### 🆚 Code Comparison
|
|
60
|
+
|
|
61
|
+
See how QHTTPX simplifies common patterns compared to Express and Fastify.
|
|
62
|
+
|
|
63
|
+
**The Task**: A simple API endpoint that reads a query parameter, sets a header, and returns JSON.
|
|
64
|
+
|
|
65
|
+
#### Express
|
|
66
|
+
```javascript
|
|
67
|
+
const express = require('express');
|
|
68
|
+
const app = express();
|
|
69
|
+
|
|
70
|
+
app.get('/hello', (req, res) => {
|
|
71
|
+
const name = req.query.name || 'World';
|
|
72
|
+
res.setHeader('X-Powered-By', 'Express');
|
|
73
|
+
res.json({ message: `Hello ${name}` });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
app.listen(3000);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Fastify
|
|
80
|
+
```javascript
|
|
81
|
+
const fastify = require('fastify')();
|
|
82
|
+
|
|
83
|
+
fastify.get('/hello', async (request, reply) => {
|
|
84
|
+
const name = request.query.name || 'World';
|
|
85
|
+
reply.header('X-Powered-By', 'Fastify');
|
|
86
|
+
return { message: `Hello ${name}` };
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
fastify.listen({ port: 3000 });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### QHTTPX
|
|
93
|
+
```typescript
|
|
94
|
+
import { app } from 'qhttpx';
|
|
95
|
+
|
|
96
|
+
app.get('/hello', ({ query, setHeader, json }) => {
|
|
97
|
+
const name = query.name || 'World';
|
|
98
|
+
setHeader('X-Powered-By', 'QHTTPX');
|
|
99
|
+
json({ message: `Hello ${name}` });
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
app.listen(3000);
|
|
103
|
+
```
|
|
104
|
+
|
|
59
105
|
---
|
|
60
106
|
|
|
61
107
|
## ✨ Key Features
|
|
@@ -92,34 +138,50 @@ npm install qhttpx
|
|
|
92
138
|
|
|
93
139
|
## ⚡ Quick Start
|
|
94
140
|
|
|
95
|
-
### 1.
|
|
96
|
-
|
|
97
|
-
Scaffold a production-ready QHTTPX application instantly using our CLI tool:
|
|
141
|
+
### 1. The Modern Way (TypeScript + tsx) - Recommended
|
|
142
|
+
Best for development and production with modern tooling.
|
|
98
143
|
|
|
144
|
+
**Option A: Scaffolding (Fastest)**
|
|
99
145
|
```bash
|
|
100
|
-
npm create qhttpx@latest
|
|
146
|
+
npm create qhttpx@latest my-app
|
|
147
|
+
cd my-app
|
|
148
|
+
npm install
|
|
149
|
+
npm run dev
|
|
101
150
|
```
|
|
102
|
-
This will set up a project with TypeScript, Request Fusion, and best practices pre-configured.
|
|
103
151
|
|
|
104
|
-
|
|
105
|
-
|
|
152
|
+
**Option B: Manual Setup**
|
|
153
|
+
```bash
|
|
154
|
+
npm install qhttpx tsx
|
|
155
|
+
```
|
|
106
156
|
|
|
157
|
+
*src/index.ts*
|
|
107
158
|
```typescript
|
|
108
|
-
import { app } from
|
|
159
|
+
import { app } from 'qhttpx';
|
|
160
|
+
|
|
161
|
+
app.get('/', ({ json }) => json({ msg: 'Works!' }));
|
|
162
|
+
app.listen(3000, () => console.log('Server running on http://localhost:3000'));
|
|
163
|
+
```
|
|
109
164
|
|
|
110
|
-
|
|
111
|
-
app.get("/", ({ json }) => json({ message: "Hello World" }));
|
|
165
|
+
Run with: `npx tsx src/index.ts`
|
|
112
166
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
167
|
+
### 2. The Classic Way (JavaScript + CommonJS)
|
|
168
|
+
For legacy environments or simple scripts.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm install qhttpx
|
|
172
|
+
```
|
|
117
173
|
|
|
118
|
-
|
|
119
|
-
|
|
174
|
+
*index.js*
|
|
175
|
+
```javascript
|
|
176
|
+
const { app } = require('qhttpx');
|
|
177
|
+
|
|
178
|
+
app.get('/', ({ json }) => json({ msg: 'Works!' }));
|
|
179
|
+
app.listen(3000, () => console.log('Running on http://localhost:3000'));
|
|
120
180
|
```
|
|
121
181
|
|
|
122
|
-
|
|
182
|
+
Run with: `node index.js`
|
|
183
|
+
|
|
184
|
+
### 3. The Advanced Way (Custom Configuration)
|
|
123
185
|
When you need specific configuration options or multiple instances.
|
|
124
186
|
|
|
125
187
|
```typescript
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const dotenv_1 = require("dotenv");
|
|
5
|
+
// Load .env
|
|
6
|
+
(0, dotenv_1.config)();
|
|
7
|
+
// 1. Initialize App (Fusion + Aegis enabled)
|
|
8
|
+
const app = (0, index_1.createHttpApp)({
|
|
9
|
+
enableRequestFusion: true, // ⚡ Auto-coalesce duplicate requests
|
|
10
|
+
metricsEnabled: true, // 📊 Expose /__qhttpx/metrics
|
|
11
|
+
rateLimit: {
|
|
12
|
+
windowMs: 15 * 60 * 1000,
|
|
13
|
+
max: 100,
|
|
14
|
+
trustProxy: true
|
|
15
|
+
},
|
|
16
|
+
cors: true, // 🛡️ Built-in CORS
|
|
17
|
+
compression: true // 🗜️ Built-in Compression (Gzip/Brotli)
|
|
18
|
+
});
|
|
19
|
+
// 2. Global Middleware (Logging is built-in by default)
|
|
20
|
+
// No manual app.use(rateLimit(...)) needed!
|
|
21
|
+
// 3. Validation Schema
|
|
22
|
+
const UserSchema = {
|
|
23
|
+
body: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
required: ['name', 'role'],
|
|
26
|
+
properties: { name: { type: 'string' }, role: { type: 'string' } }
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
// 4. Routes (Clean & Destructured)
|
|
30
|
+
app.get('/', ({ json }) => json({ status: 'online', fusion: true }));
|
|
31
|
+
// ⚡ Fused Endpoint: 1000 concurrent requests -> 1 DB execution
|
|
32
|
+
app.get('/heavy', async ({ json }) => {
|
|
33
|
+
await new Promise(r => setTimeout(r, 100)); // Simulate DB
|
|
34
|
+
json({ data: 'Expensive Result', timestamp: Date.now() });
|
|
35
|
+
});
|
|
36
|
+
// 🛡️ Validated & Typed Route
|
|
37
|
+
app.post('/users', { schema: UserSchema }, async ({ body, json }) => {
|
|
38
|
+
// Body is already validated and typed here
|
|
39
|
+
json({ created: true, user: body }, 201);
|
|
40
|
+
});
|
|
41
|
+
// � Header Auth Example
|
|
42
|
+
app.post('/auth/verify', async ({ headers, json }) => {
|
|
43
|
+
const token = headers.authorization?.replace('Bearer ', '');
|
|
44
|
+
if (!token)
|
|
45
|
+
return json({ error: 'No token provided' }, 401);
|
|
46
|
+
// Verify token...
|
|
47
|
+
json({ valid: true, token });
|
|
48
|
+
});
|
|
49
|
+
// �📂 File Uploads (Typed)
|
|
50
|
+
app.post('/upload', ({ files, json }) => {
|
|
51
|
+
if (!files?.doc)
|
|
52
|
+
return json({ error: 'No file' }, 400);
|
|
53
|
+
const doc = Array.isArray(files.doc) ? files.doc[0] : files.doc;
|
|
54
|
+
json({ filename: doc.filename, size: doc.size });
|
|
55
|
+
});
|
|
56
|
+
// 📡 WebSockets (Pub/Sub)
|
|
57
|
+
app.upgrade('/chat', (ws) => {
|
|
58
|
+
ws.join('general'); // Auto-join room
|
|
59
|
+
ws.on('message', (msg) => {
|
|
60
|
+
// Broadcast to room
|
|
61
|
+
app.websocket.to('general').emit(`Echo: ${msg}`);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
// 5. Unified Error Handling
|
|
65
|
+
app.onError(({ error, json }) => {
|
|
66
|
+
if (error instanceof index_1.HttpError) {
|
|
67
|
+
return json({
|
|
68
|
+
error: error.message,
|
|
69
|
+
code: error.code,
|
|
70
|
+
details: error.details
|
|
71
|
+
}, error.status);
|
|
72
|
+
}
|
|
73
|
+
console.error(error); // Log internal error
|
|
74
|
+
json({ error: 'Internal Server Error', handled: true }, 500);
|
|
75
|
+
});
|
|
76
|
+
// 6. Start Server
|
|
77
|
+
app.listen(3000, () => console.log('🚀 Server running on http://localhost:3000'));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)();
|
|
5
|
+
app.get('/', ({ json }) => {
|
|
6
|
+
json({ message: 'Hello from QHTTPX!' });
|
|
7
|
+
});
|
|
8
|
+
app.listen(3000, () => {
|
|
9
|
+
console.log('Server running on http://localhost:3000');
|
|
10
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)({
|
|
5
|
+
// Enable Gzip/Brotli compression
|
|
6
|
+
compression: true
|
|
7
|
+
// Or with options:
|
|
8
|
+
// compression: { threshold: 2048 } // Only compress responses > 2KB
|
|
9
|
+
});
|
|
10
|
+
app.get('/large', ({ json }) => {
|
|
11
|
+
// Generate a large response to trigger compression
|
|
12
|
+
const data = Array(1000).fill('some repeated data to compress');
|
|
13
|
+
json({ data });
|
|
14
|
+
});
|
|
15
|
+
app.listen(3000, () => {
|
|
16
|
+
console.log('Compression-enabled server running on http://localhost:3000');
|
|
17
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)({
|
|
5
|
+
// Simple: Enable CORS for all origins
|
|
6
|
+
// cors: true
|
|
7
|
+
// Advanced: Configure specific origins
|
|
8
|
+
cors: {
|
|
9
|
+
origin: ['http://localhost:5173', 'https://myapp.com'],
|
|
10
|
+
methods: ['GET', 'POST'],
|
|
11
|
+
credentials: true
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
app.get('/api/data', ({ json }) => {
|
|
15
|
+
json({ data: 'This data is accessible via CORS' });
|
|
16
|
+
});
|
|
17
|
+
app.listen(3000, () => {
|
|
18
|
+
console.log('CORS-enabled server running on http://localhost:3000');
|
|
19
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)();
|
|
5
|
+
app.get('/missing', () => {
|
|
6
|
+
throw new index_1.NotFoundException('Resource not found');
|
|
7
|
+
});
|
|
8
|
+
app.get('/private', () => {
|
|
9
|
+
throw new index_1.ForbiddenException('You do not have access');
|
|
10
|
+
});
|
|
11
|
+
// Custom global error handler
|
|
12
|
+
app.onError(({ error, json }) => {
|
|
13
|
+
if (error instanceof index_1.HttpError) {
|
|
14
|
+
return json({
|
|
15
|
+
status: 'error',
|
|
16
|
+
code: error.code,
|
|
17
|
+
message: error.message
|
|
18
|
+
}, error.status);
|
|
19
|
+
}
|
|
20
|
+
console.error('Unexpected error:', error);
|
|
21
|
+
json({ status: 'error', message: 'Internal Server Error' }, 500);
|
|
22
|
+
});
|
|
23
|
+
app.listen(3000, () => {
|
|
24
|
+
console.log('Error handling demo running on http://localhost:3000');
|
|
25
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)({
|
|
5
|
+
// Limits for uploads
|
|
6
|
+
maxBodyBytes: 10 * 1024 * 1024 // 10MB
|
|
7
|
+
});
|
|
8
|
+
app.post('/upload', ({ files, json }) => {
|
|
9
|
+
if (!files || !files.document) {
|
|
10
|
+
return json({ error: 'No file uploaded' }, 400);
|
|
11
|
+
}
|
|
12
|
+
const doc = Array.isArray(files.document) ? files.document[0] : files.document;
|
|
13
|
+
console.log(`Received file: ${doc.filename} (${doc.size} bytes)`);
|
|
14
|
+
json({
|
|
15
|
+
uploaded: true,
|
|
16
|
+
filename: doc.filename,
|
|
17
|
+
mimetype: doc.mimeType,
|
|
18
|
+
size: doc.size
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
app.listen(3000, () => {
|
|
22
|
+
console.log('Upload server running on http://localhost:3000');
|
|
23
|
+
console.log('Send POST to /upload with form-data field "document"');
|
|
24
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)({
|
|
5
|
+
// Enable Request Fusion (Request Coalescing)
|
|
6
|
+
enableRequestFusion: true
|
|
7
|
+
});
|
|
8
|
+
// Simulate a slow database call
|
|
9
|
+
const heavyTask = async () => {
|
|
10
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
11
|
+
return { data: 'Expensive Result', timestamp: Date.now() };
|
|
12
|
+
};
|
|
13
|
+
// If 1000 users hit this endpoint simultaneously,
|
|
14
|
+
// the handler runs ONLY ONCE, and the result is shared.
|
|
15
|
+
app.get('/heavy', async ({ json }) => {
|
|
16
|
+
const result = await heavyTask();
|
|
17
|
+
json(result);
|
|
18
|
+
});
|
|
19
|
+
app.listen(3000, () => {
|
|
20
|
+
console.log('Fusion-enabled server running on http://localhost:3000');
|
|
21
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)({
|
|
5
|
+
rateLimit: {
|
|
6
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
7
|
+
max: 100, // Limit each IP to 100 requests per window
|
|
8
|
+
message: 'Too many requests, please try again later.',
|
|
9
|
+
trustProxy: true // Trust X-Forwarded-For header (useful behind proxies like Nginx)
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
app.get('/', ({ json }) => {
|
|
13
|
+
json({ status: 'OK', message: 'Request accepted' });
|
|
14
|
+
});
|
|
15
|
+
app.listen(3000, () => {
|
|
16
|
+
console.log('Rate-limited server running on http://localhost:3000');
|
|
17
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)();
|
|
5
|
+
// Define a simple schema (or use Zod if configured)
|
|
6
|
+
const UserSchema = {
|
|
7
|
+
body: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
required: ['username', 'email'],
|
|
10
|
+
properties: {
|
|
11
|
+
username: { type: 'string' },
|
|
12
|
+
email: { type: 'string' }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
// Apply schema to route
|
|
17
|
+
app.post('/register', { schema: UserSchema }, ({ body, json }) => {
|
|
18
|
+
// 'body' is already validated and typed here (if using TS with inferred types)
|
|
19
|
+
json({ success: true, user: body });
|
|
20
|
+
});
|
|
21
|
+
app.listen(3000, () => {
|
|
22
|
+
console.log('Validation server running on http://localhost:3000');
|
|
23
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
const app = (0, index_1.createHttpApp)();
|
|
5
|
+
app.upgrade('/chat', (ws) => {
|
|
6
|
+
console.log('Client connected');
|
|
7
|
+
ws.join('general');
|
|
8
|
+
ws.on('message', (msg) => {
|
|
9
|
+
console.log(`Received: ${msg}`);
|
|
10
|
+
// Broadcast to everyone in 'general' room
|
|
11
|
+
app.websocket.to('general').emit(`Echo: ${msg}`);
|
|
12
|
+
});
|
|
13
|
+
ws.on('close', () => {
|
|
14
|
+
console.log('Client disconnected');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
app.listen(3000, () => {
|
|
18
|
+
console.log('WebSocket Server running on http://localhost:3000');
|
|
19
|
+
console.log('Test with a WebSocket client at ws://localhost:3000/chat');
|
|
20
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qhttpx",
|
|
3
|
+
"version": "1.9.4",
|
|
4
|
+
"gypfile": false,
|
|
5
|
+
"description": "The Ultra-Fast HTTP Framework for Node.js",
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
|
+
"types": "dist/src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/src/index.d.ts",
|
|
11
|
+
"require": "./dist/src/index.js",
|
|
12
|
+
"default": "./dist/src/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"qhttpx": "./dist/src/cli/index.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src/native",
|
|
21
|
+
"prebuilds",
|
|
22
|
+
"scripts",
|
|
23
|
+
"binding.gyp",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"docs"
|
|
28
|
+
],
|
|
29
|
+
"directories": {
|
|
30
|
+
"doc": "docs"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"prebuild": "node -e \"try{require('child_process').execSync('prebuildify --napi --strip', {stdio: 'ignore'})}catch(e){}\"",
|
|
34
|
+
"prepublishOnly": "npm run build",
|
|
35
|
+
"build": "tsc -p tsconfig.json",
|
|
36
|
+
"lint": "eslint src tests --ext .ts",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"example": "npx tsx examples/api-server.ts",
|
|
39
|
+
"bench": "npm run build && node dist/src/benchmarks/simple-json.js",
|
|
40
|
+
"bench:quantam": "npm run build && node dist/src/benchmarks/quantam-users.js",
|
|
41
|
+
"bench:ultra": "npm run build && node dist/src/benchmarks/ultra-mode.js"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"http",
|
|
45
|
+
"server",
|
|
46
|
+
"web",
|
|
47
|
+
"framework",
|
|
48
|
+
"typescript",
|
|
49
|
+
"fast",
|
|
50
|
+
"performance",
|
|
51
|
+
"high-performance",
|
|
52
|
+
"async",
|
|
53
|
+
"concurrency",
|
|
54
|
+
"request-fusion",
|
|
55
|
+
"coalescing",
|
|
56
|
+
"middleware",
|
|
57
|
+
"websocket",
|
|
58
|
+
"sse",
|
|
59
|
+
"rate-limit",
|
|
60
|
+
"rest",
|
|
61
|
+
"api",
|
|
62
|
+
"json",
|
|
63
|
+
"router",
|
|
64
|
+
"radix-tree",
|
|
65
|
+
"scheduler",
|
|
66
|
+
"ultra-fast",
|
|
67
|
+
"nodejs"
|
|
68
|
+
],
|
|
69
|
+
"author": "Quantam Open Source",
|
|
70
|
+
"license": "MIT",
|
|
71
|
+
"repository": {
|
|
72
|
+
"type": "git",
|
|
73
|
+
"url": "https://github.com/Quantam-Open-Source/qhttpx"
|
|
74
|
+
},
|
|
75
|
+
"type": "commonjs",
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"@types/autocannon": "^7.12.7",
|
|
78
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
79
|
+
"@types/busboy": "^1.5.4",
|
|
80
|
+
"@types/ioredis": "^4.28.10",
|
|
81
|
+
"@types/mongodb": "^4.0.6",
|
|
82
|
+
"@types/node": "^25.0.9",
|
|
83
|
+
"@types/pg": "^8.16.0",
|
|
84
|
+
"@types/ws": "^8.18.1",
|
|
85
|
+
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
86
|
+
"@typescript-eslint/parser": "^8.53.0",
|
|
87
|
+
"autocannon": "^8.0.0",
|
|
88
|
+
"dotenv": "^17.2.3",
|
|
89
|
+
"eslint": "^9.39.2",
|
|
90
|
+
"eslint-config-prettier": "^10.1.8",
|
|
91
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
92
|
+
"mongodb": "^7.0.0",
|
|
93
|
+
"pg": "^8.17.1",
|
|
94
|
+
"prebuildify": "^6.0.1",
|
|
95
|
+
"prettier": "^3.8.0",
|
|
96
|
+
"tsx": "^4.21.0",
|
|
97
|
+
"typescript": "^5.9.3",
|
|
98
|
+
"vitest": "^4.0.17"
|
|
99
|
+
},
|
|
100
|
+
"dependencies": {
|
|
101
|
+
"better-sqlite3": "^12.6.2",
|
|
102
|
+
"busboy": "^1.6.0",
|
|
103
|
+
"fast-json-stringify": "^5.15.1",
|
|
104
|
+
"node-addon-api": "^8.5.0",
|
|
105
|
+
"node-gyp-build": "^4.8.4",
|
|
106
|
+
"pino": "^10.2.0",
|
|
107
|
+
"pino-pretty": "^13.1.3",
|
|
108
|
+
"quantam-async": "^0.1.1",
|
|
109
|
+
"ws": "^8.19.0",
|
|
110
|
+
"zod": "^4.3.5"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const autocannon_1 = __importDefault(require("autocannon"));
|
|
7
|
+
const fastify_1 = __importDefault(require("fastify"));
|
|
8
|
+
const express_1 = __importDefault(require("express"));
|
|
9
|
+
const index_1 = require("../index");
|
|
10
|
+
async function runAutocannon(name, url) {
|
|
11
|
+
const result = await (0, autocannon_1.default)({
|
|
12
|
+
url,
|
|
13
|
+
connections: 200,
|
|
14
|
+
pipelining: 10,
|
|
15
|
+
duration: 10,
|
|
16
|
+
});
|
|
17
|
+
const total = result.requests.total;
|
|
18
|
+
const sent = result.requests.sent;
|
|
19
|
+
const rps = result.requests.average;
|
|
20
|
+
const p99 = result.latency.p99;
|
|
21
|
+
console.log(`${name} bench: total=${total} (sent=${sent}) req, ` +
|
|
22
|
+
`${rps.toFixed(0)} req/sec, p99=${p99.toFixed(1)}ms, connections=${result.connections}, pipelining=${result.pipelining}`);
|
|
23
|
+
return {
|
|
24
|
+
name,
|
|
25
|
+
total,
|
|
26
|
+
sent,
|
|
27
|
+
rps,
|
|
28
|
+
p99,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async function startQHTTPX() {
|
|
32
|
+
const payloadBuffer = Buffer.from(JSON.stringify({ message: 'hello from qhttpx' }));
|
|
33
|
+
const app = new index_1.QHTTPX({
|
|
34
|
+
maxConcurrency: 1024,
|
|
35
|
+
metricsEnabled: false,
|
|
36
|
+
jsonSerializer: () => payloadBuffer,
|
|
37
|
+
});
|
|
38
|
+
app.get('/json', (ctx) => {
|
|
39
|
+
ctx.json({ message: 'hello from qhttpx' });
|
|
40
|
+
});
|
|
41
|
+
const { port } = await app.listen(0, '127.0.0.1');
|
|
42
|
+
const url = `http://127.0.0.1:${port}/json`;
|
|
43
|
+
return { app, url };
|
|
44
|
+
}
|
|
45
|
+
async function startFastify() {
|
|
46
|
+
const app = (0, fastify_1.default)();
|
|
47
|
+
app.get('/json', async () => {
|
|
48
|
+
return { message: 'hello from fastify' };
|
|
49
|
+
});
|
|
50
|
+
await app.listen({ port: 0, host: '127.0.0.1' });
|
|
51
|
+
const address = app.server.address();
|
|
52
|
+
if (!address || typeof address === 'string') {
|
|
53
|
+
throw new Error('Fastify address not available');
|
|
54
|
+
}
|
|
55
|
+
const url = `http://127.0.0.1:${address.port}/json`;
|
|
56
|
+
return { app, url };
|
|
57
|
+
}
|
|
58
|
+
async function startExpress() {
|
|
59
|
+
const app = (0, express_1.default)();
|
|
60
|
+
app.get('/json', (_req, res) => {
|
|
61
|
+
res.json({ message: 'hello from express' });
|
|
62
|
+
});
|
|
63
|
+
const server = await new Promise((resolve) => {
|
|
64
|
+
const s = app.listen(0, '127.0.0.1', () => {
|
|
65
|
+
resolve(s);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
const address = server.address();
|
|
69
|
+
if (!address || typeof address === 'string') {
|
|
70
|
+
throw new Error('Express address not available');
|
|
71
|
+
}
|
|
72
|
+
const url = `http://127.0.0.1:${address.port}/json`;
|
|
73
|
+
return { app, url, server };
|
|
74
|
+
}
|
|
75
|
+
async function run() {
|
|
76
|
+
const results = [];
|
|
77
|
+
const qhttpx = await startQHTTPX();
|
|
78
|
+
try {
|
|
79
|
+
const r = await runAutocannon('QHTTPX', qhttpx.url);
|
|
80
|
+
results.push(r);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await qhttpx.app.close();
|
|
84
|
+
}
|
|
85
|
+
const fast = await startFastify();
|
|
86
|
+
try {
|
|
87
|
+
const r = await runAutocannon('Fastify', fast.url);
|
|
88
|
+
results.push(r);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await fast.app.close();
|
|
92
|
+
}
|
|
93
|
+
const exp = await startExpress();
|
|
94
|
+
try {
|
|
95
|
+
const r = await runAutocannon('Express', exp.url);
|
|
96
|
+
results.push(r);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
await new Promise((resolve, reject) => {
|
|
100
|
+
exp.server.close((err) => {
|
|
101
|
+
if (err) {
|
|
102
|
+
reject(err);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
resolve();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
console.log('\nSummary:');
|
|
111
|
+
for (const r of results) {
|
|
112
|
+
console.log(`${r.name}: ${r.rps.toFixed(0)} req/sec, p99=${r.p99.toFixed(1)}ms, total=${r.total}`);
|
|
113
|
+
}
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
run().catch((err) => {
|
|
117
|
+
console.error(err);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|