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.
- package/CHANGELOG.md +18 -0
- package/README.md +33 -22
- package/assets/logo.svg +24 -10
- package/dist/examples/api-server.d.ts +1 -0
- package/dist/examples/api-server.js +56 -0
- package/dist/package.json +1 -1
- package/dist/src/benchmarks/quantam-users.d.ts +1 -0
- package/dist/src/benchmarks/simple-json.d.ts +1 -0
- package/dist/src/benchmarks/ultra-mode.d.ts +1 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/client/index.d.ts +17 -0
- package/dist/src/core/batch.d.ts +24 -0
- package/dist/src/core/body-parser.d.ts +15 -0
- package/dist/src/core/buffer-pool.d.ts +41 -0
- package/dist/src/core/config.d.ts +7 -0
- package/dist/src/core/fusion.d.ts +14 -0
- package/dist/src/core/logger.d.ts +22 -0
- package/dist/src/core/metrics.d.ts +45 -0
- package/dist/src/core/resources.d.ts +9 -0
- package/dist/src/core/scheduler.d.ts +34 -0
- package/dist/src/core/scope.d.ts +26 -0
- package/dist/src/core/serializer.d.ts +10 -0
- package/dist/src/core/server.d.ts +90 -0
- package/dist/src/core/server.js +152 -106
- package/dist/src/core/stream.d.ts +15 -0
- package/dist/src/core/tasks.d.ts +29 -0
- package/dist/src/core/types.d.ts +135 -0
- package/dist/src/core/websocket.d.ts +25 -0
- package/dist/src/core/worker-queue.d.ts +41 -0
- package/dist/src/database/adapters/memory.d.ts +21 -0
- package/dist/src/database/adapters/mongo.d.ts +11 -0
- package/dist/src/database/adapters/postgres.d.ts +10 -0
- package/dist/src/database/adapters/sqlite.d.ts +10 -0
- package/dist/src/database/coalescer.d.ts +14 -0
- package/dist/src/database/manager.d.ts +35 -0
- package/dist/src/database/types.d.ts +20 -0
- package/dist/src/index.d.ts +45 -0
- package/dist/src/index.js +8 -1
- package/dist/src/middleware/compression.d.ts +6 -0
- package/dist/src/middleware/cors.d.ts +11 -0
- package/dist/src/middleware/presets.d.ts +13 -0
- package/dist/src/middleware/rate-limit.d.ts +32 -0
- package/dist/src/middleware/security.d.ts +22 -0
- package/dist/src/middleware/static.d.ts +11 -0
- package/dist/src/openapi/generator.d.ts +19 -0
- package/dist/src/router/radix-router.d.ts +18 -0
- package/dist/src/router/radix-tree.d.ts +16 -0
- package/dist/src/router/router.d.ts +33 -0
- package/dist/src/testing/index.d.ts +25 -0
- package/dist/src/utils/cookies.d.ts +3 -0
- package/dist/src/utils/logger.d.ts +12 -0
- package/dist/src/utils/signals.d.ts +6 -0
- package/dist/src/utils/sse.d.ts +6 -0
- package/dist/src/validation/index.d.ts +3 -0
- package/dist/src/validation/simple.d.ts +5 -0
- package/dist/src/validation/types.d.ts +32 -0
- package/dist/src/validation/zod.d.ts +4 -0
- package/dist/src/views/index.d.ts +1 -0
- package/dist/src/views/types.d.ts +3 -0
- package/dist/tests/adapters.test.d.ts +1 -0
- package/dist/tests/batch.test.d.ts +1 -0
- package/dist/tests/body-parser.test.d.ts +1 -0
- package/dist/tests/compression-sse.test.d.ts +1 -0
- package/dist/tests/cookies.test.d.ts +1 -0
- package/dist/tests/cors.test.d.ts +1 -0
- package/dist/tests/database.test.d.ts +1 -0
- package/dist/tests/dx.test.d.ts +1 -0
- package/dist/tests/dx.test.js +100 -50
- package/dist/tests/ecosystem.test.d.ts +1 -0
- package/dist/tests/features.test.d.ts +1 -0
- package/dist/tests/fusion.test.d.ts +1 -0
- package/dist/tests/http-basic.test.d.ts +1 -0
- package/dist/tests/logger.test.d.ts +1 -0
- package/dist/tests/middleware.test.d.ts +1 -0
- package/dist/tests/observability.test.d.ts +1 -0
- package/dist/tests/openapi.test.d.ts +1 -0
- package/dist/tests/plugin.test.d.ts +1 -0
- package/dist/tests/plugins.test.d.ts +1 -0
- package/dist/tests/rate-limit.test.d.ts +1 -0
- package/dist/tests/resources.test.d.ts +1 -0
- package/dist/tests/scheduler.test.d.ts +1 -0
- package/dist/tests/schema-routes.test.d.ts +1 -0
- package/dist/tests/security.test.d.ts +1 -0
- package/dist/tests/server-db.test.d.ts +1 -0
- package/dist/tests/smoke.test.d.ts +1 -0
- package/dist/tests/sqlite-fusion.test.d.ts +1 -0
- package/dist/tests/static.test.d.ts +1 -0
- package/dist/tests/stream.test.d.ts +1 -0
- package/dist/tests/task-metrics.test.d.ts +1 -0
- package/dist/tests/tasks.test.d.ts +1 -0
- package/dist/tests/testing.test.d.ts +1 -0
- package/dist/tests/validation.test.d.ts +1 -0
- package/dist/tests/websocket.test.d.ts +1 -0
- package/dist/vitest.config.d.ts +2 -0
- package/examples/api-server.ts +44 -236
- package/package.json +1 -1
- package/src/core/server.ts +221 -103
- package/src/core/types.ts +17 -4
- package/src/index.ts +8 -0
- package/tests/dx.test.ts +109 -57
- package/tsconfig.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.8.4] - 2026-01-20
|
|
6
|
+
**"The Usability III Update"**
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- **Flexible Route Signatures**: Routes now support `(path, options, handler)` signature for cleaner validation configuration.
|
|
10
|
+
- **Simplified Listen**: `app.listen(port, callback)` is now supported, matching standard patterns.
|
|
11
|
+
- **Updated Examples**: `examples/api-server.ts` rewritten to demonstrate the "Rapid Way" (Fusion, Validation, WebSockets, Typed Files).
|
|
12
|
+
|
|
13
|
+
## [1.8.3] - 2026-01-20
|
|
14
|
+
**"The Developer Experience II Update"**
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- **Unified Destructuring**: Added comprehensive support for destructuring `ctx` everywhere, including route handlers, error handlers, and middleware.
|
|
18
|
+
- **Middleware DX**: Middleware can now access `next` via destructuring: `app.use(({ next }) => next())`.
|
|
19
|
+
- **Type Safety**: `ctx.files` is now strongly typed as `QHTTPXFile | QHTTPXFile[]` (no more `any`).
|
|
20
|
+
- **Unified Error Context**: `onError` now receives a `QHTTPXErrorContext` containing the `error` object (`({ error, json }) => ...`).
|
|
21
|
+
- **Aliases**: Added `app.onError` and `app.notFound` aliases for cleaner code.
|
|
22
|
+
|
|
5
23
|
## [1.8.2] - 2026-01-20
|
|
6
24
|
**"The Usability Update"**
|
|
7
25
|
|
package/README.md
CHANGED
|
@@ -77,30 +77,42 @@ npm install qhttpx
|
|
|
77
77
|
|
|
78
78
|
## ⚡ Quick Start
|
|
79
79
|
|
|
80
|
-
### 1. The
|
|
81
|
-
|
|
80
|
+
### 1. The Rapid Way (Recommended)
|
|
81
|
+
Get up and running instantly with the singleton instance and destructuring support.
|
|
82
82
|
|
|
83
83
|
```typescript
|
|
84
|
-
|
|
85
|
-
import QHTTPX from "qhttpx";
|
|
84
|
+
import { app } from "qhttpx";
|
|
86
85
|
|
|
87
|
-
//
|
|
88
|
-
|
|
86
|
+
// Concise, destructured handlers
|
|
87
|
+
app.get("/", ({ json }) => json({ message: "Hello World" }));
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
// Unified error handling
|
|
90
|
+
app.onError(({ error, json }) => {
|
|
91
|
+
json({ error: "Something went wrong", details: error }, 500);
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
//
|
|
95
|
-
app.
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
// Start listening
|
|
95
|
+
app.listen(3000, () => console.log("Server running on http://localhost:3000"));
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 2. The Custom Way
|
|
99
|
+
When you need specific configuration options or multiple instances.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { createHttpApp } from "qhttpx";
|
|
103
|
+
|
|
104
|
+
const app = createHttpApp({
|
|
105
|
+
enableRequestFusion: true, // Enable coalescing
|
|
106
|
+
maxBodySize: "1mb"
|
|
98
107
|
});
|
|
99
108
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
app.get("/users", async ({ json, query }) => {
|
|
110
|
+
const { page } = query;
|
|
111
|
+
// ...
|
|
112
|
+
json({ page });
|
|
103
113
|
});
|
|
114
|
+
|
|
115
|
+
app.listen(3000);
|
|
104
116
|
```
|
|
105
117
|
|
|
106
118
|
### 2. The Scalable Way (Cluster Mode)
|
|
@@ -143,13 +155,12 @@ app.get('/events', (ctx) => {
|
|
|
143
155
|
});
|
|
144
156
|
|
|
145
157
|
// WebSockets with Rooms
|
|
146
|
-
app.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
158
|
+
app.upgrade('/chat', (ws) => {
|
|
159
|
+
ws.join('general');
|
|
160
|
+
ws.on('message', (msg) => {
|
|
161
|
+
// Broadcast to 'general' room
|
|
162
|
+
app.websocket.to('general').emit(`Echo: ${msg}`);
|
|
163
|
+
});
|
|
153
164
|
});
|
|
154
165
|
```
|
|
155
166
|
|
package/assets/logo.svg
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
3
|
+
<!-- Creator: CorelDRAW -->
|
|
4
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="28.726mm" height="11.2072mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
|
|
5
|
+
viewBox="0 0 2872.6 1120.72"
|
|
6
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
7
|
+
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
|
|
8
|
+
<defs>
|
|
9
|
+
<style type="text/css">
|
|
10
|
+
<![CDATA[
|
|
11
|
+
.fil1 {fill:#2B2A29;fill-rule:nonzero}
|
|
12
|
+
.fil0 {fill:#00A0E3;fill-rule:nonzero}
|
|
13
|
+
]]>
|
|
14
|
+
</style>
|
|
15
|
+
</defs>
|
|
16
|
+
<g id="Layer_x0020_1">
|
|
17
|
+
<metadata id="CorelCorpID_0Corel-Layer"/>
|
|
18
|
+
<path class="fil0" d="M414.71 674.26l197.01 0 87.46 110.27 107.44 123.19 171.39 213 -219.58 0 -120.61 -144.12 -68.18 -98.97 -154.93 -203.37zm74.29 379.69c-92.16,0 -175.15,-20.45 -248.97,-61.36 -73.59,-40.91 -132.13,-100.62 -175.15,-179.15 -43.26,-78.52 -64.89,-173.74 -64.89,-285.88 0,-112.85 21.63,-208.53 64.89,-287.06 43.02,-78.52 101.56,-138.23 175.15,-179.14 73.82,-40.91 156.81,-61.36 248.97,-61.36 91.93,0 174.68,20.45 248.27,61.36 73.82,40.91 132.12,100.62 175.15,179.14 42.79,78.53 64.41,174.21 64.41,287.06 0,112.61 -21.62,208.07 -64.41,286.59 -43.03,78.52 -101.33,138 -175.15,178.67 -73.59,40.68 -156.34,61.13 -248.27,61.13zm0 -213.7c49.61,0 92.4,-11.76 128.13,-35.74 35.97,-23.98 63.48,-59.01 82.76,-105.8 19.27,-46.54 28.91,-103.67 28.91,-171.15 0,-67.94 -9.64,-125.31 -28.91,-172.09 -19.28,-46.79 -46.79,-82.29 -82.76,-106.03 -35.73,-23.98 -78.52,-35.97 -128.13,-35.97 -50.07,0 -92.86,12.22 -128.83,36.2 -35.74,24.22 -63.24,59.48 -82.52,106.03 -19.28,46.79 -28.92,103.92 -28.92,171.86 0,67.48 9.64,124.61 28.92,170.92 19.28,46.31 46.78,81.58 82.52,105.79 35.97,23.98 78.76,35.98 128.83,35.98z"/>
|
|
19
|
+
<path class="fil1" d="M1106.72 521.64l0 310.02 -19.05 0 0 -615.95 19.05 0 0 253.01 -4.93 0c9.87,-35.28 29.49,-61.81 58.84,-79.45 29.35,-17.63 61.52,-26.38 96.52,-26.38 31.75,0 59.69,6.63 83.82,20.03 23.99,13.41 43.04,31.9 56.73,55.6 13.83,23.71 20.74,51.37 20.74,83.12l0 310.02 -19.05 0 0 -310.02c0,-41.91 -13.26,-75.64 -39.65,-101.32 -26.53,-25.68 -60.68,-38.38 -102.59,-38.52 -28.65,0.14 -54.33,6.06 -77.05,18.06 -22.72,11.99 -40.64,28.5 -53.76,49.67 -13.12,21.03 -19.62,45.01 -19.62,72.11z"/>
|
|
20
|
+
<path id="_1" class="fil1" d="M1687.12 369.52l0 18.91 -182.74 0 0 -18.91 182.74 0zm-114.86 -110.07l19.05 0 0 484.16c0,27.51 6.91,47.83 20.74,60.96 13.97,13.12 32.74,16.65 56.45,10.58 2.25,-0.56 4.65,-1.13 7.05,-1.83 2.54,-0.71 4.8,-1.42 7.06,-1.98l4.51 18.63c-2.4,0.56 -4.94,1.27 -7.62,1.83 -2.68,0.71 -5.22,1.41 -7.62,1.98 -30.9,7.33 -55.17,2.25 -72.95,-15.53 -17.78,-17.78 -26.67,-42.61 -26.67,-74.64l0 -484.16z"/>
|
|
21
|
+
<path id="_2" class="fil1" d="M1890.46 369.52l0 18.91 -182.74 0 0 -18.91 182.74 0zm-114.86 -110.07l19.05 0 0 484.16c0,27.51 6.91,47.83 20.74,60.96 13.97,13.12 32.74,16.65 56.45,10.58 2.25,-0.56 4.65,-1.13 7.05,-1.83 2.54,-0.71 4.8,-1.42 7.06,-1.98l4.51 18.63c-2.4,0.56 -4.94,1.27 -7.62,1.83 -2.68,0.71 -5.22,1.41 -7.62,1.98 -30.9,7.33 -55.17,2.25 -72.95,-15.53 -17.78,-17.78 -26.67,-42.61 -26.67,-74.64l0 -484.16z"/>
|
|
22
|
+
<path id="_3" class="fil1" d="M1988.81 1004.52l0 -635 19.05 0 0 118.11 2.4 0c8.05,-25.54 19.76,-47.7 35,-66.46 15.24,-18.77 33.3,-33.17 54.19,-43.18 20.74,-10.16 43.46,-15.1 68.01,-15.1 36.13,0 67.45,10.44 93.98,31.32 26.67,21.03 47.27,49.53 61.81,85.66 14.68,36.12 21.87,76.76 21.87,121.92 0,45.44 -7.19,86.36 -21.59,122.63 -14.53,36.26 -34.99,64.77 -61.66,85.79 -26.53,20.89 -58,31.33 -94.41,31.33 -24.83,0 -47.55,-5.08 -68.3,-15.24 -20.6,-10.16 -38.52,-24.7 -53.76,-43.61 -15.1,-18.9 -26.81,-41.2 -35.14,-66.74l-2.4 0 0 288.57 -19.05 0zm178.65 -181.89c32.46,0 60.54,-9.74 84.25,-29.21 23.7,-19.47 42.05,-45.86 55.03,-79.16 12.98,-33.31 19.47,-70.84 19.47,-112.47 0,-41.63 -6.49,-79.02 -19.47,-112.18 -12.98,-33.31 -31.33,-59.55 -55.03,-78.74 -23.71,-19.34 -51.79,-28.93 -84.25,-29.07 -32.59,0.14 -60.68,9.73 -84.52,29.07 -23.85,19.19 -42.34,45.43 -55.46,78.74 -13.12,33.16 -19.62,70.55 -19.62,112.18 0,41.63 6.5,79.16 19.48,112.47 12.84,33.3 31.33,59.69 55.17,79.16 23.85,19.47 52.07,29.21 84.95,29.21z"/>
|
|
23
|
+
<path id="_4" class="fil1" d="M2402.13 831.66l228.61 -319.62 0 13.27 -221.97 -309.6 23.14 0 138.15 192.61c8.18,11.29 16.37,22.58 24.41,33.73 7.9,11.15 15.95,22.44 23.99,33.87 7.9,11.43 15.8,23.28 23.56,35.28l-8.74 0c8.04,-12 15.94,-23.85 23.84,-35.28 7.76,-11.43 15.67,-22.72 23.71,-33.87 8.04,-11.15 15.95,-22.44 23.99,-33.73l138.01 -192.61 23.14 0 -221.97 310.44 0 -14.11 228.6 319.62 -23.14 0 -146.76 -205.04c-7.62,-11 -15.38,-22.01 -23.14,-32.87 -7.76,-10.87 -15.38,-21.88 -23.14,-33.02 -7.76,-11.29 -15.38,-22.58 -23.14,-34.15l8.32 0c-7.76,11.57 -15.38,22.86 -23,34.15 -7.62,11.14 -15.1,22.15 -22.72,33.02 -7.62,10.86 -15.38,21.87 -23.42,32.87l-147.18 205.04 -23.15 0z"/>
|
|
24
|
+
</g>
|
|
11
25
|
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../src/index");
|
|
4
|
+
// 1. Initialize App (Fusion + Aegis enabled)
|
|
5
|
+
const app = (0, index_1.createHttpApp)({
|
|
6
|
+
enableRequestFusion: true, // ⚡ Auto-coalesce duplicate requests
|
|
7
|
+
metricsEnabled: true // 📊 Expose /__qhttpx/metrics
|
|
8
|
+
});
|
|
9
|
+
// 2. Global Middleware (Logging & Rate Limit)
|
|
10
|
+
app.use(async ({ req, next }) => {
|
|
11
|
+
console.log(`[${req.method}] ${req.url}`);
|
|
12
|
+
if (next)
|
|
13
|
+
await next();
|
|
14
|
+
});
|
|
15
|
+
// 3. Validation Schema
|
|
16
|
+
const UserSchema = {
|
|
17
|
+
body: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
required: ['name', 'role'],
|
|
20
|
+
properties: { name: { type: 'string' }, role: { type: 'string' } }
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
// 4. Routes (Clean & Destructured)
|
|
24
|
+
app.get('/', ({ json }) => json({ status: 'online', fusion: true }));
|
|
25
|
+
// ⚡ Fused Endpoint: 1000 concurrent requests -> 1 DB execution
|
|
26
|
+
app.get('/heavy', async ({ json }) => {
|
|
27
|
+
await new Promise(r => setTimeout(r, 100)); // Simulate DB
|
|
28
|
+
json({ data: 'Expensive Result', timestamp: Date.now() });
|
|
29
|
+
});
|
|
30
|
+
// 🛡️ Validated & Typed Route
|
|
31
|
+
app.post('/users', { schema: UserSchema }, async ({ body, json }) => {
|
|
32
|
+
// Body is already validated and typed here
|
|
33
|
+
json({ created: true, user: body }, 201);
|
|
34
|
+
});
|
|
35
|
+
// 📂 File Uploads (Typed)
|
|
36
|
+
app.post('/upload', ({ files, json }) => {
|
|
37
|
+
if (!files?.doc)
|
|
38
|
+
return json({ error: 'No file' }, 400);
|
|
39
|
+
const doc = Array.isArray(files.doc) ? files.doc[0] : files.doc;
|
|
40
|
+
json({ filename: doc.filename, size: doc.size });
|
|
41
|
+
});
|
|
42
|
+
// 📡 WebSockets (Pub/Sub)
|
|
43
|
+
app.upgrade('/chat', (ws) => {
|
|
44
|
+
ws.join('general'); // Auto-join room
|
|
45
|
+
ws.on('message', (msg) => {
|
|
46
|
+
// Broadcast to room
|
|
47
|
+
app.websocket.to('general').emit(`Echo: ${msg}`);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
// 5. Unified Error Handling
|
|
51
|
+
app.onError(({ error, json }) => {
|
|
52
|
+
console.error(error); // Log internal error
|
|
53
|
+
json({ error: 'Internal Server Error', handled: true }, 500);
|
|
54
|
+
});
|
|
55
|
+
// 6. Start Server
|
|
56
|
+
app.listen(3000, () => console.log('🚀 Server running on http://localhost:3000'));
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qhttpx",
|
|
3
|
-
"version": "1.8.
|
|
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",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type InferRequestType<T> = T extends {
|
|
2
|
+
request: infer R;
|
|
3
|
+
} ? R : never;
|
|
4
|
+
export type InferResponseType<T> = T extends {
|
|
5
|
+
response: infer R;
|
|
6
|
+
} ? R : never;
|
|
7
|
+
export declare function hc<T extends Record<string, any>>(baseUrl: string, options?: RequestInit): Client<T>;
|
|
8
|
+
type Client<T> = {
|
|
9
|
+
[K in keyof T]: T[K] extends Function ? never : Client<T[K]>;
|
|
10
|
+
} & {
|
|
11
|
+
$get: (args?: any) => Promise<any>;
|
|
12
|
+
$post: (args?: any) => Promise<any>;
|
|
13
|
+
$put: (args?: any) => Promise<any>;
|
|
14
|
+
$delete: (args?: any) => Promise<any>;
|
|
15
|
+
$patch: (args?: any) => Promise<any>;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { QHTTPXContext, QHTTPXOpHandler } from './types';
|
|
2
|
+
import { DatabaseManager } from '../database/manager';
|
|
3
|
+
export declare class BatchExecutor {
|
|
4
|
+
private ops;
|
|
5
|
+
private dbManager?;
|
|
6
|
+
private coalescers;
|
|
7
|
+
constructor(dbManager?: DatabaseManager);
|
|
8
|
+
register(name: string, handler: QHTTPXOpHandler): void;
|
|
9
|
+
handleBatch(ctx: QHTTPXContext, batch: Array<{
|
|
10
|
+
op: string;
|
|
11
|
+
params: unknown;
|
|
12
|
+
id?: unknown;
|
|
13
|
+
}>): Promise<{
|
|
14
|
+
results: ({
|
|
15
|
+
error: string;
|
|
16
|
+
id: unknown;
|
|
17
|
+
result?: undefined;
|
|
18
|
+
} | {
|
|
19
|
+
result: any;
|
|
20
|
+
id: unknown;
|
|
21
|
+
error?: undefined;
|
|
22
|
+
})[];
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IncomingMessage } from 'http';
|
|
2
|
+
export type BodyParserOptions = {
|
|
3
|
+
maxBodyBytes?: number;
|
|
4
|
+
};
|
|
5
|
+
export type ParsedBody = {
|
|
6
|
+
body: unknown;
|
|
7
|
+
files?: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
export interface IBodyParser {
|
|
10
|
+
parse(req: IncomingMessage, options?: BodyParserOptions): Promise<any>;
|
|
11
|
+
}
|
|
12
|
+
export declare class BodyParser {
|
|
13
|
+
static parse(req: IncomingMessage, options?: BodyParserOptions): Promise<any>;
|
|
14
|
+
private static parseMultipart;
|
|
15
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffer pool for response bodies (small, medium, large).
|
|
3
|
+
* Reuses allocated buffers to reduce GC pressure.
|
|
4
|
+
*/
|
|
5
|
+
export type BufferPoolConfig = {
|
|
6
|
+
smallSize?: number;
|
|
7
|
+
mediumSize?: number;
|
|
8
|
+
largeSize?: number;
|
|
9
|
+
smallPoolSize?: number;
|
|
10
|
+
mediumPoolSize?: number;
|
|
11
|
+
largePoolSize?: number;
|
|
12
|
+
};
|
|
13
|
+
export declare class BufferPool {
|
|
14
|
+
private readonly smallSize;
|
|
15
|
+
private readonly mediumSize;
|
|
16
|
+
private readonly largeSize;
|
|
17
|
+
private readonly smallBuffers;
|
|
18
|
+
private readonly mediumBuffers;
|
|
19
|
+
private readonly largeBuffers;
|
|
20
|
+
private readonly smallPoolSize;
|
|
21
|
+
private readonly mediumPoolSize;
|
|
22
|
+
private readonly largePoolSize;
|
|
23
|
+
constructor(config?: BufferPoolConfig);
|
|
24
|
+
/**
|
|
25
|
+
* Acquire a buffer suitable for the given size.
|
|
26
|
+
* Returns a buffer from the appropriate pool.
|
|
27
|
+
*/
|
|
28
|
+
acquire(size: number): Buffer;
|
|
29
|
+
/**
|
|
30
|
+
* Release a buffer back to the appropriate pool.
|
|
31
|
+
*/
|
|
32
|
+
release(buffer: Buffer): void;
|
|
33
|
+
/**
|
|
34
|
+
* Get the number of available buffers in each pool.
|
|
35
|
+
*/
|
|
36
|
+
getPoolStatus(): {
|
|
37
|
+
small: number;
|
|
38
|
+
medium: number;
|
|
39
|
+
large: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { QHTTPXContext } from './types';
|
|
2
|
+
export interface RequestFusionOptions {
|
|
3
|
+
windowMs?: number;
|
|
4
|
+
vary?: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare class RequestFusion {
|
|
7
|
+
private inflight;
|
|
8
|
+
private cache;
|
|
9
|
+
private options;
|
|
10
|
+
constructor(options?: boolean | RequestFusionOptions);
|
|
11
|
+
coalesce(ctx: QHTTPXContext, next: (ctx: QHTTPXContext) => Promise<void> | void): Promise<void>;
|
|
12
|
+
private applyResult;
|
|
13
|
+
private getKey;
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
3
|
+
export interface LoggerOptions {
|
|
4
|
+
level?: LogLevel;
|
|
5
|
+
pretty?: boolean;
|
|
6
|
+
name?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class Logger {
|
|
9
|
+
private pino;
|
|
10
|
+
constructor(options?: LoggerOptions);
|
|
11
|
+
info(msg: string, ...args: any[]): void;
|
|
12
|
+
info(obj: object, msg?: string, ...args: any[]): void;
|
|
13
|
+
error(msg: string, ...args: any[]): void;
|
|
14
|
+
error(obj: object, msg?: string, ...args: any[]): void;
|
|
15
|
+
warn(msg: string, ...args: any[]): void;
|
|
16
|
+
warn(obj: object, msg?: string, ...args: any[]): void;
|
|
17
|
+
debug(msg: string, ...args: any[]): void;
|
|
18
|
+
debug(obj: object, msg?: string, ...args: any[]): void;
|
|
19
|
+
fatal(msg: string, ...args: any[]): void;
|
|
20
|
+
fatal(obj: object, msg?: string, ...args: any[]): void;
|
|
21
|
+
child(bindings: pino.Bindings): Logger;
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TaskEngine, TaskMetrics } from './tasks';
|
|
2
|
+
import { Scheduler } from './scheduler';
|
|
3
|
+
export type LatencySnapshot = {
|
|
4
|
+
p50: number | null;
|
|
5
|
+
p95: number | null;
|
|
6
|
+
p99: number | null;
|
|
7
|
+
};
|
|
8
|
+
export type MetricsSnapshot = {
|
|
9
|
+
totalRequests: number;
|
|
10
|
+
inFlightRequests: number;
|
|
11
|
+
totalErrors: number;
|
|
12
|
+
totalTimeouts: number;
|
|
13
|
+
requestsPerSecond: number;
|
|
14
|
+
latency: LatencySnapshot;
|
|
15
|
+
scheduler: {
|
|
16
|
+
inFlight: number;
|
|
17
|
+
};
|
|
18
|
+
tasks?: TaskMetrics;
|
|
19
|
+
memory: {
|
|
20
|
+
rssBytes: number;
|
|
21
|
+
heapUsedBytes: number;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export declare class Metrics {
|
|
25
|
+
private readonly scheduler;
|
|
26
|
+
private readonly taskEngine?;
|
|
27
|
+
private readonly latencies;
|
|
28
|
+
private readonly maxLatencies;
|
|
29
|
+
private readonly enabled;
|
|
30
|
+
private totalRequests;
|
|
31
|
+
private inFlightRequests;
|
|
32
|
+
private totalErrors;
|
|
33
|
+
private totalTimeouts;
|
|
34
|
+
constructor(scheduler: Scheduler, options?: {
|
|
35
|
+
maxLatencies?: number;
|
|
36
|
+
enabled?: boolean;
|
|
37
|
+
}, taskEngine?: TaskEngine);
|
|
38
|
+
onRequestStart(): void;
|
|
39
|
+
onRequestEnd(durationMs: number, statusCode: number): void;
|
|
40
|
+
onTimeout(): void;
|
|
41
|
+
snapshot(): MetricsSnapshot;
|
|
42
|
+
private recordLatency;
|
|
43
|
+
private latencySnapshot;
|
|
44
|
+
private percentile;
|
|
45
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type WorkerSetting = 'auto' | number;
|
|
2
|
+
export declare function calculateWorkerCount(setting: WorkerSetting): number;
|
|
3
|
+
export type ResourceThresholds = {
|
|
4
|
+
maxRssBytes?: number;
|
|
5
|
+
};
|
|
6
|
+
export type ResourceSample = {
|
|
7
|
+
rssBytes: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function isResourceOverloaded(sample: ResourceSample, thresholds: ResourceThresholds): boolean;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { RoutePriority } from './types';
|
|
2
|
+
export type SchedulerOptions = {
|
|
3
|
+
maxConcurrency?: number;
|
|
4
|
+
workers?: number;
|
|
5
|
+
};
|
|
6
|
+
export type RunOptions = {
|
|
7
|
+
priority?: RoutePriority;
|
|
8
|
+
onOverloaded?: () => void;
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
onTimeout?: () => void;
|
|
11
|
+
};
|
|
12
|
+
export type SchedulerStats = {
|
|
13
|
+
inFlight: number;
|
|
14
|
+
maxConcurrency: number;
|
|
15
|
+
workers: number;
|
|
16
|
+
perWorkerStats: {
|
|
17
|
+
workerId: number;
|
|
18
|
+
queued: number;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
21
|
+
export declare class Scheduler {
|
|
22
|
+
private inFlight;
|
|
23
|
+
private readonly maxConcurrency;
|
|
24
|
+
private readonly workerCount;
|
|
25
|
+
private readonly perWorkerQueues;
|
|
26
|
+
private nextWorkerIndex;
|
|
27
|
+
constructor(options?: SchedulerOptions);
|
|
28
|
+
getCurrentInFlight(): number;
|
|
29
|
+
/**
|
|
30
|
+
* Get scheduler statistics (queued tasks per worker, etc.)
|
|
31
|
+
*/
|
|
32
|
+
getStats(): SchedulerStats;
|
|
33
|
+
run(task: () => void | Promise<void>, options: RunOptions): Promise<void>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { QHTTPX } from './server';
|
|
2
|
+
import { QHTTPXHandler, QHTTPXRouteOptions, QHTTPXMiddleware, QHTTPXPlugin, QHTTPXPluginOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A Scope represents a prefixed or isolated context for plugins.
|
|
5
|
+
* It proxies methods to the main QHTTPX instance but handles prefixing.
|
|
6
|
+
*/
|
|
7
|
+
export declare class QHTTPXScope {
|
|
8
|
+
private readonly app;
|
|
9
|
+
private readonly prefix;
|
|
10
|
+
constructor(app: QHTTPX, prefix?: string);
|
|
11
|
+
/**
|
|
12
|
+
* Registers a sub-plugin within this scope.
|
|
13
|
+
* Prefixes are concatenated (e.g. /v1 + /users = /v1/users).
|
|
14
|
+
*/
|
|
15
|
+
register<Options extends QHTTPXPluginOptions>(plugin: QHTTPXPlugin<Options>, options?: Options): Promise<void>;
|
|
16
|
+
use(middleware: QHTTPXMiddleware): void;
|
|
17
|
+
get(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
18
|
+
post(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
19
|
+
put(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
20
|
+
delete(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
21
|
+
patch(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
22
|
+
options(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
23
|
+
head(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
24
|
+
getApp(): QHTTPX;
|
|
25
|
+
private joinPaths;
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast JSON serializer using fast-json-stringify
|
|
3
|
+
* For best performance, use schema-based stringifiers per route
|
|
4
|
+
*/
|
|
5
|
+
export declare function fastJsonStringify(value: unknown, schema?: unknown): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get a pre-compiled stringifier for a specific schema
|
|
8
|
+
* Use this in route handlers for maximum performance
|
|
9
|
+
*/
|
|
10
|
+
export declare function getStringifier(schema: unknown): (value: unknown) => string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import { WebSocketManager } from './websocket';
|
|
3
|
+
import { OpenAPIOptions } from '../openapi/generator';
|
|
4
|
+
import { HTTPMethod, QHTTPXErrorHandler, QHTTPXHandler, QHTTPXMethodNotAllowedHandler, QHTTPXMiddleware, QHTTPXNotFoundHandler, QHTTPXOptions, QHTTPXRouteOptions, QHTTPXPlugin, QHTTPXPluginOptions } from './types';
|
|
5
|
+
import { Logger } from './logger';
|
|
6
|
+
export declare class QHTTPX {
|
|
7
|
+
private readonly options;
|
|
8
|
+
private readonly server;
|
|
9
|
+
readonly logger: Logger;
|
|
10
|
+
private readonly router;
|
|
11
|
+
private readonly scheduler;
|
|
12
|
+
private readonly middlewares;
|
|
13
|
+
private readonly workerCount;
|
|
14
|
+
private readonly tasks;
|
|
15
|
+
private readonly metrics;
|
|
16
|
+
private readonly contextPool;
|
|
17
|
+
private readonly bufferPool;
|
|
18
|
+
private pipelineRunner;
|
|
19
|
+
private errorHandler?;
|
|
20
|
+
private notFoundHandler?;
|
|
21
|
+
private methodNotAllowedHandler?;
|
|
22
|
+
private readonly tracer?;
|
|
23
|
+
private readonly onStartHooks;
|
|
24
|
+
private readonly onBeforeShutdownHooks;
|
|
25
|
+
private readonly onShutdownHooks;
|
|
26
|
+
private nextRequestId;
|
|
27
|
+
private readonly wsManager;
|
|
28
|
+
private readonly ultraMode;
|
|
29
|
+
private readonly batchExecutor?;
|
|
30
|
+
private readonly fusion?;
|
|
31
|
+
private readonly validator;
|
|
32
|
+
constructor(options?: QHTTPXOptions);
|
|
33
|
+
get serverInstance(): http.Server;
|
|
34
|
+
get websocket(): WebSocketManager;
|
|
35
|
+
setErrorHandler(handler: QHTTPXErrorHandler): void;
|
|
36
|
+
setNotFoundHandler(handler: QHTTPXNotFoundHandler): void;
|
|
37
|
+
setMethodNotAllowedHandler(handler: QHTTPXMethodNotAllowedHandler): void;
|
|
38
|
+
set404Handler(handler: QHTTPXNotFoundHandler): void;
|
|
39
|
+
set405Handler(handler: QHTTPXMethodNotAllowedHandler): void;
|
|
40
|
+
/**
|
|
41
|
+
* Alias for setErrorHandler
|
|
42
|
+
*/
|
|
43
|
+
onError(handler: QHTTPXErrorHandler): void;
|
|
44
|
+
/**
|
|
45
|
+
* Alias for setNotFoundHandler
|
|
46
|
+
*/
|
|
47
|
+
notFound(handler: QHTTPXNotFoundHandler): void;
|
|
48
|
+
onStart(hook: () => void | Promise<void>): void;
|
|
49
|
+
onBeforeShutdown(hook: () => void | Promise<void>): void;
|
|
50
|
+
onShutdown(hook: () => void | Promise<void>): void;
|
|
51
|
+
upgrade(path: string, handler: import('./websocket').WSHandler): void;
|
|
52
|
+
use(middleware: QHTTPXMiddleware): void;
|
|
53
|
+
private compileMiddlewarePipeline;
|
|
54
|
+
private compileRoutePipeline;
|
|
55
|
+
_registerRoute(method: HTTPMethod, path: string, handlerOrOptions: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
56
|
+
register<Options extends QHTTPXPluginOptions>(plugin: QHTTPXPlugin<Options>, options?: Options): Promise<void>;
|
|
57
|
+
private registerRoute;
|
|
58
|
+
get(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
59
|
+
get(path: string, config: import('./types').QHTTPXRouteConfig, handler: QHTTPXHandler): void;
|
|
60
|
+
post(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
61
|
+
post(path: string, config: import('./types').QHTTPXRouteConfig, handler: QHTTPXHandler): void;
|
|
62
|
+
put(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
63
|
+
put(path: string, config: import('./types').QHTTPXRouteConfig, handler: QHTTPXHandler): void;
|
|
64
|
+
delete(path: string, handler: QHTTPXHandler | QHTTPXRouteOptions): void;
|
|
65
|
+
delete(path: string, config: import('./types').QHTTPXRouteConfig, handler: QHTTPXHandler): void;
|
|
66
|
+
route(path: string): {
|
|
67
|
+
get(handler: QHTTPXHandler | QHTTPXRouteOptions): /*elided*/ any;
|
|
68
|
+
post(handler: QHTTPXHandler | QHTTPXRouteOptions): /*elided*/ any;
|
|
69
|
+
put(handler: QHTTPXHandler | QHTTPXRouteOptions): /*elided*/ any;
|
|
70
|
+
delete(handler: QHTTPXHandler | QHTTPXRouteOptions): /*elided*/ any;
|
|
71
|
+
};
|
|
72
|
+
task(name: string, handler: import('./types').QHTTPXTaskHandler, options?: import('./types').QHTTPXTaskOptions): void;
|
|
73
|
+
enqueue(name: string, payload: unknown): Promise<void>;
|
|
74
|
+
op(name: string, handler: import('./types').QHTTPXOpHandler): void;
|
|
75
|
+
private registerInternalRoutes;
|
|
76
|
+
getOpenAPI(options: OpenAPIOptions): object;
|
|
77
|
+
listen(port: number, hostnameOrCallback?: string | (() => void), callback?: () => void): Promise<{
|
|
78
|
+
port: number;
|
|
79
|
+
}>;
|
|
80
|
+
close(): Promise<void>;
|
|
81
|
+
shutdown(): Promise<void>;
|
|
82
|
+
private createContext;
|
|
83
|
+
private acquireContext;
|
|
84
|
+
private releaseContext;
|
|
85
|
+
private handleRequest;
|
|
86
|
+
private handleUpgrade;
|
|
87
|
+
private runLifecycleHooks;
|
|
88
|
+
private handleError;
|
|
89
|
+
private generateRequestId;
|
|
90
|
+
}
|