qhttpx 1.8.2 → 1.8.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +33 -22
  3. package/assets/logo.svg +24 -10
  4. package/dist/examples/api-server.d.ts +1 -0
  5. package/dist/examples/api-server.js +56 -0
  6. package/dist/package.json +1 -1
  7. package/dist/src/benchmarks/quantam-users.d.ts +1 -0
  8. package/dist/src/benchmarks/simple-json.d.ts +1 -0
  9. package/dist/src/benchmarks/ultra-mode.d.ts +1 -0
  10. package/dist/src/cli/index.d.ts +2 -0
  11. package/dist/src/client/index.d.ts +17 -0
  12. package/dist/src/core/batch.d.ts +24 -0
  13. package/dist/src/core/body-parser.d.ts +15 -0
  14. package/dist/src/core/buffer-pool.d.ts +41 -0
  15. package/dist/src/core/config.d.ts +7 -0
  16. package/dist/src/core/fusion.d.ts +14 -0
  17. package/dist/src/core/logger.d.ts +22 -0
  18. package/dist/src/core/metrics.d.ts +45 -0
  19. package/dist/src/core/resources.d.ts +9 -0
  20. package/dist/src/core/scheduler.d.ts +34 -0
  21. package/dist/src/core/scope.d.ts +26 -0
  22. package/dist/src/core/serializer.d.ts +10 -0
  23. package/dist/src/core/server.d.ts +90 -0
  24. package/dist/src/core/server.js +152 -106
  25. package/dist/src/core/stream.d.ts +15 -0
  26. package/dist/src/core/tasks.d.ts +29 -0
  27. package/dist/src/core/types.d.ts +135 -0
  28. package/dist/src/core/websocket.d.ts +25 -0
  29. package/dist/src/core/worker-queue.d.ts +41 -0
  30. package/dist/src/database/adapters/memory.d.ts +21 -0
  31. package/dist/src/database/adapters/mongo.d.ts +11 -0
  32. package/dist/src/database/adapters/postgres.d.ts +10 -0
  33. package/dist/src/database/adapters/sqlite.d.ts +10 -0
  34. package/dist/src/database/coalescer.d.ts +14 -0
  35. package/dist/src/database/manager.d.ts +35 -0
  36. package/dist/src/database/types.d.ts +20 -0
  37. package/dist/src/index.d.ts +45 -0
  38. package/dist/src/index.js +8 -1
  39. package/dist/src/middleware/compression.d.ts +6 -0
  40. package/dist/src/middleware/cors.d.ts +11 -0
  41. package/dist/src/middleware/presets.d.ts +13 -0
  42. package/dist/src/middleware/rate-limit.d.ts +32 -0
  43. package/dist/src/middleware/security.d.ts +22 -0
  44. package/dist/src/middleware/static.d.ts +11 -0
  45. package/dist/src/openapi/generator.d.ts +19 -0
  46. package/dist/src/router/radix-router.d.ts +18 -0
  47. package/dist/src/router/radix-tree.d.ts +16 -0
  48. package/dist/src/router/router.d.ts +33 -0
  49. package/dist/src/testing/index.d.ts +25 -0
  50. package/dist/src/utils/cookies.d.ts +3 -0
  51. package/dist/src/utils/logger.d.ts +12 -0
  52. package/dist/src/utils/signals.d.ts +6 -0
  53. package/dist/src/utils/sse.d.ts +6 -0
  54. package/dist/src/validation/index.d.ts +3 -0
  55. package/dist/src/validation/simple.d.ts +5 -0
  56. package/dist/src/validation/types.d.ts +32 -0
  57. package/dist/src/validation/zod.d.ts +4 -0
  58. package/dist/src/views/index.d.ts +1 -0
  59. package/dist/src/views/types.d.ts +3 -0
  60. package/dist/tests/adapters.test.d.ts +1 -0
  61. package/dist/tests/batch.test.d.ts +1 -0
  62. package/dist/tests/body-parser.test.d.ts +1 -0
  63. package/dist/tests/compression-sse.test.d.ts +1 -0
  64. package/dist/tests/cookies.test.d.ts +1 -0
  65. package/dist/tests/cors.test.d.ts +1 -0
  66. package/dist/tests/database.test.d.ts +1 -0
  67. package/dist/tests/dx.test.d.ts +1 -0
  68. package/dist/tests/dx.test.js +100 -50
  69. package/dist/tests/ecosystem.test.d.ts +1 -0
  70. package/dist/tests/features.test.d.ts +1 -0
  71. package/dist/tests/fusion.test.d.ts +1 -0
  72. package/dist/tests/http-basic.test.d.ts +1 -0
  73. package/dist/tests/logger.test.d.ts +1 -0
  74. package/dist/tests/middleware.test.d.ts +1 -0
  75. package/dist/tests/observability.test.d.ts +1 -0
  76. package/dist/tests/openapi.test.d.ts +1 -0
  77. package/dist/tests/plugin.test.d.ts +1 -0
  78. package/dist/tests/plugins.test.d.ts +1 -0
  79. package/dist/tests/rate-limit.test.d.ts +1 -0
  80. package/dist/tests/resources.test.d.ts +1 -0
  81. package/dist/tests/scheduler.test.d.ts +1 -0
  82. package/dist/tests/schema-routes.test.d.ts +1 -0
  83. package/dist/tests/security.test.d.ts +1 -0
  84. package/dist/tests/server-db.test.d.ts +1 -0
  85. package/dist/tests/smoke.test.d.ts +1 -0
  86. package/dist/tests/sqlite-fusion.test.d.ts +1 -0
  87. package/dist/tests/static.test.d.ts +1 -0
  88. package/dist/tests/stream.test.d.ts +1 -0
  89. package/dist/tests/task-metrics.test.d.ts +1 -0
  90. package/dist/tests/tasks.test.d.ts +1 -0
  91. package/dist/tests/testing.test.d.ts +1 -0
  92. package/dist/tests/validation.test.d.ts +1 -0
  93. package/dist/tests/websocket.test.d.ts +1 -0
  94. package/dist/vitest.config.d.ts +2 -0
  95. package/examples/api-server.ts +44 -236
  96. package/package.json +1 -1
  97. package/src/core/server.ts +221 -103
  98. package/src/core/types.ts +17 -4
  99. package/src/index.ts +8 -0
  100. package/tests/dx.test.ts +109 -57
  101. package/tsconfig.json +2 -1
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
@@ -1,254 +1,62 @@
1
- import { createHash } from 'crypto';
2
- import { IncomingMessage } from 'http';
3
- import { Duplex } from 'stream';
4
- import { createHttpApp, HttpError } from '../src/index';
1
+ import { createHttpApp } from '../src/index';
5
2
 
6
- // 1. Initialize the application (includes CORS, Security Headers, Logging)
7
- const app = createHttpApp({
8
- metricsEnabled: true,
9
- workers: 1,
3
+ // 1. Initialize App (Fusion + Aegis enabled)
4
+ const app = createHttpApp({
5
+ enableRequestFusion: true, // ⚔ Auto-coalesce duplicate requests
6
+ metricsEnabled: true // šŸ“Š Expose /__qhttpx/metrics
10
7
  });
11
8
 
12
- // 2. Mock Database
13
- const users = new Map<string, { id: string; name: string; role: string }>();
14
-
15
- // Seed some data
16
- users.set('1', { id: '1', name: 'Alice', role: 'admin' });
17
- users.set('2', { id: '2', name: 'Bob', role: 'user' });
18
-
19
- // 3. Register Background Tasks
20
- app.task('send-email', async (payload: any) => {
21
- // Simulate heavy work
22
- await new Promise((resolve) => setTimeout(resolve, 500));
23
- console.log(`[Background Job] Sending email to ${payload.email}: "${payload.subject}"`);
24
- });
25
-
26
- // 4. Define Routes
27
-
28
- // Root
29
- app.get('/', (ctx) => {
30
- ctx.json({
31
- message: 'Welcome to QHTTPX Example API',
32
- endpoints: [
33
- 'GET /api/users',
34
- 'POST /api/users',
35
- 'GET /api/users/:id',
36
- 'POST /api/jobs/email',
37
- 'WS /ws',
38
- 'GET /api/cookies',
39
- 'GET /api/redirect'
40
- ],
41
- documentation: 'https://github.com/your-repo/qhttpx'
42
- });
43
- });
44
-
45
- app.get('/api/cookies', (ctx) => {
46
- // Read cookies
47
- const visited = parseInt(ctx.cookies['visited'] || '0');
48
-
49
- // Set cookie
50
- ctx.setCookie('visited', (visited + 1).toString(), {
51
- path: '/',
52
- maxAge: 3600, // 1 hour
53
- httpOnly: true,
54
- });
55
-
56
- ctx.json({
57
- message: 'Cookie demo',
58
- visited: visited + 1,
59
- allCookies: ctx.cookies
60
- });
9
+ // 2. Global Middleware (Logging & Rate Limit)
10
+ app.use(async ({ req, next }) => {
11
+ console.log(`[${req.method}] ${req.url}`);
12
+ if (next) await next();
61
13
  });
62
14
 
63
- app.get('/api/redirect', (ctx) => {
64
- ctx.redirect('/api/users');
65
- });
66
-
67
- // User Routes using the Chainable Builder
68
- app.route('/api/users')
69
- .get((ctx) => {
70
- const userList = Array.from(users.values());
71
- ctx.json({ data: userList, count: userList.length });
72
- })
73
- .post(async (ctx) => {
74
- if (!ctx.body || typeof ctx.body !== 'object') {
75
- ctx.json({ error: 'Invalid body' }, 400);
76
- return;
77
- }
78
-
79
- const body = ctx.body as any;
80
- if (!body.name || !body.role) {
81
- ctx.json({ error: 'Missing name or role' }, 400);
82
- return;
83
- }
84
-
85
- const id = (users.size + 1).toString();
86
- const newUser = { id, name: body.name, role: body.role };
87
- users.set(id, newUser);
88
-
89
- ctx.json({ message: 'User created', user: newUser }, 201);
90
- });
91
-
92
- app.route('/api/users/:id')
93
- .get((ctx) => {
94
- const { id } = ctx.params;
95
- const user = users.get(id);
96
-
97
- if (!user) {
98
- ctx.json({ error: 'User not found' }, 404);
99
- return;
100
- }
101
-
102
- ctx.json({ data: user });
103
- })
104
- .delete((ctx) => {
105
- const { id } = ctx.params;
106
- if (users.delete(id)) {
107
- ctx.json({ message: 'User deleted' });
108
- } else {
109
- ctx.json({ error: 'User not found' }, 404);
110
- }
111
- });
112
-
113
- // Job Route
114
- app.post('/api/jobs/email', async (ctx) => {
115
- const body = ctx.body as any;
116
- if (!body.email) {
117
- ctx.json({ error: 'Email required' }, 400);
118
- return;
15
+ // 3. Validation Schema
16
+ const UserSchema = {
17
+ body: {
18
+ type: 'object',
19
+ required: ['name', 'role'],
20
+ properties: { name: { type: 'string' }, role: { type: 'string' } }
119
21
  }
22
+ };
120
23
 
121
- await app.enqueue('send-email', {
122
- email: body.email,
123
- subject: 'Welcome to QHTTPX!'
124
- });
24
+ // 4. Routes (Clean & Destructured)
25
+ app.get('/', ({ json }) => json({ status: 'online', fusion: true }));
125
26
 
126
- ctx.json({ message: 'Email job queued', status: 'pending' }, 202);
27
+ // ⚔ Fused Endpoint: 1000 concurrent requests -> 1 DB execution
28
+ app.get('/heavy', async ({ json }) => {
29
+ await new Promise(r => setTimeout(r, 100)); // Simulate DB
30
+ json({ data: 'Expensive Result', timestamp: Date.now() });
127
31
  });
128
32
 
129
- // WebSocket Route
130
- app.upgrade('/ws', handleWebSocketEcho);
131
-
132
- // Error handling demo
133
- app.get('/error', () => {
134
- throw new Error('Something went wrong!');
33
+ // šŸ›”ļø Validated & Typed Route
34
+ app.post('/users', { schema: UserSchema }, async ({ body, json }) => {
35
+ // Body is already validated and typed here
36
+ json({ created: true, user: body }, 201);
135
37
  });
136
38
 
137
- // Custom Error Handlers (optional, defaults are good)
138
- app.setNotFoundHandler((ctx) => {
139
- ctx.json({ error: 'Not Found', path: ctx.url.pathname }, 404);
39
+ // šŸ“‚ File Uploads (Typed)
40
+ app.post('/upload', ({ files, json }) => {
41
+ if (!files?.doc) return json({ error: 'No file' }, 400);
42
+ const doc = Array.isArray(files.doc) ? files.doc[0] : files.doc;
43
+ json({ filename: doc.filename, size: doc.size });
140
44
  });
141
45
 
142
- app.setErrorHandler((err, ctx) => {
143
- if (err instanceof HttpError) {
144
- ctx.json({ error: err.message, code: err.code, details: err.details }, err.status);
145
- return;
146
- }
147
- ctx.json({ error: 'Internal Server Error' }, 500);
46
+ // šŸ“” WebSockets (Pub/Sub)
47
+ app.upgrade('/chat', (ws) => {
48
+ ws.join('general'); // Auto-join room
49
+ ws.on('message', (msg) => {
50
+ // Broadcast to room
51
+ app.websocket.to('general').emit(`Echo: ${msg}`);
52
+ });
148
53
  });
149
54
 
150
- // 5. Start Server
151
- const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000;
152
- app.listen(PORT, '0.0.0.0').then(({ port }) => {
153
- console.log(`\nšŸš€ Server running at http://localhost:${port}`);
154
- console.log(` Try: curl http://localhost:${port}/api/users`);
55
+ // 5. Unified Error Handling
56
+ app.onError(({ error, json }) => {
57
+ console.error(error); // Log internal error
58
+ json({ error: 'Internal Server Error', handled: true }, 500);
155
59
  });
156
60
 
157
- // --- Helpers ---
158
-
159
- function createWebSocketAccept(key: string): string {
160
- return createHash('sha1')
161
- .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
162
- .digest('base64');
163
- }
164
-
165
- function handleWebSocketEcho(req: IncomingMessage, socket: Duplex, head: Buffer) {
166
- void head;
167
-
168
- const rawKeyHeader = req.headers['sec-websocket-key'];
169
- let keyHeader: string | undefined;
170
- if (typeof rawKeyHeader === 'string') {
171
- keyHeader = rawKeyHeader;
172
- } else if (Array.isArray(rawKeyHeader)) {
173
- const values = rawKeyHeader as string[];
174
- if (values.length > 0) {
175
- keyHeader = values[0];
176
- }
177
- }
178
-
179
- const key = keyHeader ?? '';
180
- const accept = createWebSocketAccept(key);
181
- const responseLines = [
182
- 'HTTP/1.1 101 Switching Protocols',
183
- 'Upgrade: websocket',
184
- 'Connection: Upgrade',
185
- `Sec-WebSocket-Accept: ${accept}`,
186
- '',
187
- '',
188
- ];
189
- socket.write(responseLines.join('\r\n'));
190
-
191
- socket.on('data', (buffer) => {
192
- if (buffer.length < 2) return;
193
-
194
- const opcode = buffer[0] & 0x0f;
195
- const masked = (buffer[1] & 0x80) !== 0;
196
- let payloadLength = buffer[1] & 0x7f;
197
- let offset = 2;
198
-
199
- if (payloadLength === 126) {
200
- if (buffer.length < offset + 2) return;
201
- payloadLength = buffer.readUInt16BE(offset);
202
- offset += 2;
203
- } else if (payloadLength === 127) {
204
- return; // Too large for this demo
205
- }
206
-
207
- if (buffer.length < offset + payloadLength) return;
208
-
209
- let maskingKey: Buffer | undefined;
210
- if (masked) {
211
- maskingKey = buffer.subarray(offset, offset + 4);
212
- offset += 4;
213
- }
214
-
215
- const payload = buffer.subarray(offset, offset + payloadLength);
216
-
217
- if (masked && maskingKey) {
218
- for (let i = 0; i < payload.length; i += 1) {
219
- payload[i] ^= maskingKey[i % 4];
220
- }
221
- }
222
-
223
- if (opcode === 0x8) { // Close
224
- socket.end();
225
- return;
226
- }
227
-
228
- if (opcode === 0x1) { // Text
229
- const message = payload.toString('utf8');
230
- const textBytes = Buffer.from(message, 'utf8');
231
- const length = textBytes.length;
232
-
233
- let header: Buffer;
234
- if (length < 126) {
235
- header = Buffer.alloc(2);
236
- header[0] = 0x81;
237
- header[1] = length;
238
- } else if (length < 65536) {
239
- header = Buffer.alloc(4);
240
- header[0] = 0x81;
241
- header[1] = 126;
242
- header.writeUInt16BE(length, 2);
243
- } else {
244
- return; // Too large
245
- }
246
-
247
- const frame = Buffer.concat([header, textBytes]);
248
- socket.write(frame);
249
- }
250
- });
251
-
252
- socket.on('end', () => socket.end());
253
- socket.on('error', () => socket.destroy());
254
- }
61
+ // 6. Start Server
62
+ app.listen(3000, () => console.log('šŸš€ Server running on http://localhost:3000'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qhttpx",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "The High-Performance Hybrid HTTP Runtime for Node.js. Built for extreme concurrency, request fusion, and zero-overhead scaling.",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",