qhttpx 1.8.5 → 1.8.11

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 (161) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +72 -52
  3. package/binding.gyp +18 -0
  4. package/dist/examples/api-server.js +29 -8
  5. package/dist/examples/basic.d.ts +1 -0
  6. package/dist/examples/basic.js +10 -0
  7. package/dist/examples/compression.d.ts +1 -0
  8. package/dist/examples/compression.js +17 -0
  9. package/dist/examples/cors.d.ts +1 -0
  10. package/dist/examples/cors.js +19 -0
  11. package/dist/examples/errors.d.ts +1 -0
  12. package/dist/examples/errors.js +25 -0
  13. package/dist/examples/file-upload.d.ts +1 -0
  14. package/dist/examples/file-upload.js +24 -0
  15. package/dist/examples/fusion.d.ts +1 -0
  16. package/dist/examples/fusion.js +21 -0
  17. package/dist/examples/rate-limiting.d.ts +1 -0
  18. package/dist/examples/rate-limiting.js +17 -0
  19. package/dist/examples/validation.d.ts +1 -0
  20. package/dist/examples/validation.js +23 -0
  21. package/dist/examples/websockets.d.ts +1 -0
  22. package/dist/examples/websockets.js +20 -0
  23. package/dist/package.json +11 -1
  24. package/dist/src/benchmarks/simple-json.js +6 -4
  25. package/dist/src/cli/index.js +33 -11
  26. package/dist/src/core/errors.d.ts +34 -0
  27. package/dist/src/core/errors.js +70 -0
  28. package/dist/src/core/native-adapter.d.ts +11 -0
  29. package/dist/src/core/native-adapter.js +211 -0
  30. package/dist/src/core/server.d.ts +52 -4
  31. package/dist/src/core/server.js +389 -261
  32. package/dist/src/core/types.d.ts +37 -0
  33. package/dist/src/index.d.ts +6 -1
  34. package/dist/src/index.js +19 -3
  35. package/dist/src/middleware/compression.d.ts +1 -5
  36. package/dist/src/middleware/cors.d.ts +1 -10
  37. package/dist/src/middleware/presets.d.ts +4 -1
  38. package/dist/src/middleware/presets.js +22 -3
  39. package/dist/src/middleware/rate-limit.d.ts +1 -19
  40. package/dist/src/middleware/rate-limit.js +6 -0
  41. package/dist/src/middleware/security.d.ts +1 -2
  42. package/dist/src/native/index.d.ts +29 -0
  43. package/dist/src/native/index.js +64 -0
  44. package/dist/src/router/radix-tree.d.ts +2 -0
  45. package/dist/src/router/radix-tree.js +54 -4
  46. package/dist/src/router/router.d.ts +1 -0
  47. package/dist/src/router/router.js +42 -2
  48. package/dist/tests/native-adapter.test.d.ts +1 -0
  49. package/dist/tests/native-adapter.test.js +71 -0
  50. package/dist/tests/resources.test.js +3 -0
  51. package/dist/tests/security.test.js +2 -2
  52. package/docs/AEGIS.md +34 -9
  53. package/docs/BENCHMARKS.md +8 -7
  54. package/docs/ERRORS.md +112 -0
  55. package/docs/FUSION.md +68 -0
  56. package/docs/MIDDLEWARE.md +65 -0
  57. package/docs/ROUTING.md +70 -0
  58. package/docs/STATIC.md +61 -0
  59. package/docs/WEBSOCKETS.md +76 -0
  60. package/package.json +11 -1
  61. package/src/native/README.md +31 -0
  62. package/src/native/addon.cc +8 -0
  63. package/src/native/index.ts +78 -0
  64. package/src/native/picohttpparser.c +608 -0
  65. package/src/native/picohttpparser.h +71 -0
  66. package/src/native/server.cc +262 -0
  67. package/src/native/server.h +30 -0
  68. package/.eslintrc.json +0 -22
  69. package/.github/workflows/ci.yml +0 -32
  70. package/.github/workflows/npm-publish.yml +0 -37
  71. package/.github/workflows/release.yml +0 -21
  72. package/.prettierrc +0 -7
  73. package/assets/logo.svg +0 -25
  74. package/eslint.config.cjs +0 -26
  75. package/examples/api-server.ts +0 -62
  76. package/src/benchmarks/quantam-users.ts +0 -70
  77. package/src/benchmarks/simple-json.ts +0 -71
  78. package/src/benchmarks/ultra-mode.ts +0 -127
  79. package/src/cli/index.ts +0 -214
  80. package/src/client/index.ts +0 -93
  81. package/src/core/batch.ts +0 -110
  82. package/src/core/body-parser.ts +0 -151
  83. package/src/core/buffer-pool.ts +0 -96
  84. package/src/core/config.ts +0 -60
  85. package/src/core/fusion.ts +0 -210
  86. package/src/core/logger.ts +0 -70
  87. package/src/core/metrics.ts +0 -166
  88. package/src/core/resources.ts +0 -38
  89. package/src/core/scheduler.ts +0 -126
  90. package/src/core/scope.ts +0 -87
  91. package/src/core/serializer.ts +0 -41
  92. package/src/core/server.ts +0 -1234
  93. package/src/core/stream.ts +0 -111
  94. package/src/core/tasks.ts +0 -138
  95. package/src/core/types.ts +0 -192
  96. package/src/core/websocket.ts +0 -112
  97. package/src/core/worker-queue.ts +0 -90
  98. package/src/database/adapters/memory.ts +0 -99
  99. package/src/database/adapters/mongo.ts +0 -116
  100. package/src/database/adapters/postgres.ts +0 -86
  101. package/src/database/adapters/sqlite.ts +0 -44
  102. package/src/database/coalescer.ts +0 -153
  103. package/src/database/manager.ts +0 -97
  104. package/src/database/types.ts +0 -24
  105. package/src/index.ts +0 -58
  106. package/src/middleware/compression.ts +0 -147
  107. package/src/middleware/cors.ts +0 -98
  108. package/src/middleware/presets.ts +0 -50
  109. package/src/middleware/rate-limit.ts +0 -106
  110. package/src/middleware/security.ts +0 -109
  111. package/src/middleware/static.ts +0 -216
  112. package/src/openapi/generator.ts +0 -167
  113. package/src/router/radix-router.ts +0 -119
  114. package/src/router/radix-tree.ts +0 -106
  115. package/src/router/router.ts +0 -190
  116. package/src/testing/index.ts +0 -104
  117. package/src/utils/cookies.ts +0 -67
  118. package/src/utils/logger.ts +0 -59
  119. package/src/utils/signals.ts +0 -45
  120. package/src/utils/sse.ts +0 -41
  121. package/src/validation/index.ts +0 -3
  122. package/src/validation/simple.ts +0 -93
  123. package/src/validation/types.ts +0 -38
  124. package/src/validation/zod.ts +0 -14
  125. package/src/views/index.ts +0 -1
  126. package/src/views/types.ts +0 -4
  127. package/tests/adapters.test.ts +0 -120
  128. package/tests/batch.test.ts +0 -139
  129. package/tests/body-parser.test.ts +0 -83
  130. package/tests/compression-sse.test.ts +0 -98
  131. package/tests/cookies.test.ts +0 -74
  132. package/tests/cors.test.ts +0 -79
  133. package/tests/database.test.ts +0 -90
  134. package/tests/dx.test.ts +0 -130
  135. package/tests/ecosystem.test.ts +0 -156
  136. package/tests/features.test.ts +0 -51
  137. package/tests/fusion.test.ts +0 -121
  138. package/tests/http-basic.test.ts +0 -161
  139. package/tests/logger.test.ts +0 -48
  140. package/tests/middleware.test.ts +0 -137
  141. package/tests/observability.test.ts +0 -91
  142. package/tests/openapi.test.ts +0 -74
  143. package/tests/plugin.test.ts +0 -85
  144. package/tests/plugins.test.ts +0 -93
  145. package/tests/rate-limit.test.ts +0 -97
  146. package/tests/resources.test.ts +0 -64
  147. package/tests/scheduler.test.ts +0 -71
  148. package/tests/schema-routes.test.ts +0 -89
  149. package/tests/security.test.ts +0 -128
  150. package/tests/server-db.test.ts +0 -72
  151. package/tests/smoke.test.ts +0 -9
  152. package/tests/sqlite-fusion.test.ts +0 -106
  153. package/tests/static.test.ts +0 -111
  154. package/tests/stream.test.ts +0 -58
  155. package/tests/task-metrics.test.ts +0 -78
  156. package/tests/tasks.test.ts +0 -90
  157. package/tests/testing.test.ts +0 -53
  158. package/tests/validation.test.ts +0 -126
  159. package/tests/websocket.test.ts +0 -132
  160. package/tsconfig.json +0 -17
  161. package/vitest.config.ts +0 -9
package/docs/AEGIS.md CHANGED
@@ -12,7 +12,25 @@
12
12
 
13
13
  ## Usage
14
14
 
15
- Import `rateLimit` from the middleware package:
15
+ ### Zero-Config (Recommended)
16
+
17
+ The easiest way to enable Aegis is via the `createHttpApp` factory:
18
+
19
+ ```typescript
20
+ import { createHttpApp } from 'qhttpx';
21
+
22
+ const app = createHttpApp({
23
+ rateLimit: {
24
+ windowMs: 60 * 1000, // 1 minute
25
+ max: 100, // Limit each IP to 100 requests per window
26
+ trustProxy: true // Enable if behind Nginx/Cloudflare
27
+ }
28
+ });
29
+ ```
30
+
31
+ ### Manual Middleware
32
+
33
+ For more control, import `rateLimit` from the middleware package:
16
34
 
17
35
  ```typescript
18
36
  import { QHTTPX } from 'qhttpx';
@@ -26,17 +44,24 @@ app.use(rateLimit({
26
44
  max: 100, // Limit each IP to 100 requests per window
27
45
  message: { error: 'Too many requests, slow down!' }
28
46
  }));
47
+ ```
29
48
 
30
- // 2. API Key Limits (Tiered)
31
- app.use(rateLimit({
32
- windowMs: 60 * 60 * 1000, // 1 hour
33
- max: 5000,
34
- keyGenerator: (ctx) => ctx.req.headers['x-api-key'] || ctx.req.socket.remoteAddress
35
- }));
49
+ ## Proxy Support
50
+
51
+ If your app sits behind a reverse proxy (Nginx, AWS ALB, Cloudflare), you must configure **Proxy Trust** so Aegis sees the real user IP instead of the load balancer's IP.
36
52
 
37
- app.get('/', (ctx) => ctx.json({ message: 'Welcome!' }));
53
+ ```typescript
54
+ // In createHttpApp
55
+ const app = createHttpApp({
56
+ rateLimit: {
57
+ trustProxy: true // Trusts X-Forwarded-For header
58
+ }
59
+ });
38
60
 
39
- app.listen(3000);
61
+ // Or manually
62
+ app.use(rateLimit({
63
+ trustProxy: true
64
+ }));
40
65
  ```
41
66
 
42
67
  ## Options
@@ -23,14 +23,15 @@ This will:
23
23
  - Run an `autocannon` benchmark against `GET /json`
24
24
  - Shut down the server when the run completes
25
25
 
26
- ## Scenario
26
+ ## Latest Results (Local Windows Environment)
27
27
 
28
- The current scenario measures:
28
+ Configuration: 100 connections, 10 pipelining, 40s duration (Average of 2 runs, taking the second result)
29
29
 
30
- - Simple JSON endpoint: `GET /json`
31
- - Connections: 100
32
- - Pipelining: 10
33
- - Duration: 10 seconds
30
+ | Framework | Req/Sec | Latency (Avg) | Throughput |
31
+ | :--- | :--- | :--- | :--- |
32
+ | **Fastify** | 16,050 | 256.78 ms | 3.00 Mb/s |
33
+ | **QHTTPX** | **14,179** | **291.13 ms** | **3.02 Mb/s** |
34
+ | **Hono** | 14,176 | 294.09 ms | 2.43 Mb/s |
35
+ | **Express** | 10,450 | 407.12 ms | 1.94 Mb/s |
34
36
 
35
- You can adjust these parameters in `src/benchmarks/simple-json.ts`.
36
37
 
package/docs/ERRORS.md ADDED
@@ -0,0 +1,112 @@
1
+ # Error Handling
2
+
3
+ QHTTPX provides a robust and unified error handling system designed to make building reliable APIs simple and consistent.
4
+
5
+ ## Standard Exceptions
6
+
7
+ Gone are the days of manually setting status codes and message strings. QHTTPX exports a set of **Standard HTTP Exceptions** that cover most common API scenarios.
8
+
9
+ These exceptions automatically set the correct HTTP status code and structure the JSON response.
10
+
11
+ ### Available Exceptions
12
+
13
+ Import them directly from `qhttpx`:
14
+
15
+ ```typescript
16
+ import {
17
+ BadRequestException,
18
+ UnauthorizedException,
19
+ ForbiddenException,
20
+ NotFoundException,
21
+ MethodNotAllowedException,
22
+ ConflictException,
23
+ PayloadTooLargeException,
24
+ UnsupportedMediaTypeException,
25
+ TooManyRequestsException,
26
+ InternalServerErrorException,
27
+ ServiceUnavailableException
28
+ } from 'qhttpx';
29
+ ```
30
+
31
+ ### Usage in Routes
32
+
33
+ Simply `throw` an exception within your handler. QHTTPX will catch it and format the response.
34
+
35
+ ```typescript
36
+ app.get('/users/:id', ({ params }) => {
37
+ const user = findUser(params.id);
38
+
39
+ if (!user) {
40
+ throw new NotFoundException(`User with ID ${params.id} not found`);
41
+ }
42
+
43
+ if (user.isLocked) {
44
+ throw new ForbiddenException('This account is locked');
45
+ }
46
+
47
+ return user;
48
+ });
49
+ ```
50
+
51
+ **Response Example (`NotFoundException`):**
52
+ ```json
53
+ {
54
+ "error": "Not Found",
55
+ "code": "NOT_FOUND",
56
+ "details": "User with ID 123 not found",
57
+ "statusCode": 404
58
+ }
59
+ ```
60
+
61
+ ## Global Error Handler
62
+
63
+ You can define a global error handler to intercept all errors (both standard exceptions and unexpected runtime errors) to customize logging or response formatting.
64
+
65
+ Use `app.onError()`:
66
+
67
+ ```typescript
68
+ app.onError(({ error, json }) => {
69
+ console.error('Captured Error:', error);
70
+
71
+ // Check if it's a known HTTP error
72
+ if (error instanceof HttpError) {
73
+ return json({
74
+ status: 'error',
75
+ message: error.message,
76
+ code: error.code
77
+ }, error.statusCode);
78
+ }
79
+
80
+ // Handle unknown/unexpected errors
81
+ return json({
82
+ status: 'critical',
83
+ message: 'Internal Server Error'
84
+ }, 500);
85
+ });
86
+ ```
87
+
88
+ ## Async Error Handling
89
+
90
+ QHTTPX handles `async` errors automatically. You don't need `try/catch` blocks for every route unless you want specific local handling. If an async function rejects, the Global Error Handler will catch it.
91
+
92
+ ```typescript
93
+ // This works automatically!
94
+ app.get('/db-query', async () => {
95
+ const data = await db.query('SELECT * FROM users'); // If this throws, it's caught.
96
+ return data;
97
+ });
98
+ ```
99
+
100
+ ## Validation Errors
101
+
102
+ When using the built-in validation system, validation failures automatically throw a `BadRequestException` containing detailed error messages about which fields failed.
103
+
104
+ ```json
105
+ {
106
+ "error": "Bad Request",
107
+ "code": "BAD_REQUEST",
108
+ "details": [
109
+ { "field": "email", "message": "Invalid email format" }
110
+ ]
111
+ }
112
+ ```
package/docs/FUSION.md ADDED
@@ -0,0 +1,68 @@
1
+ # Request Fusion Engine (Layer 2 Coalescing)
2
+
3
+ **Request Fusion** is QHTTPX's flagship performance feature. It is a "Layer 2 Coalescing" engine designed to protect your database and downstream services from the "Thundering Herd" problem.
4
+
5
+ ## The Problem: The Thundering Herd
6
+
7
+ Imagine your API serves a popular resource, like "Trending News" (`GET /api/trending`).
8
+ During a viral event, you might receive **10,000 requests per second** for this *exact same resource*.
9
+
10
+ In traditional frameworks (Express/Fastify):
11
+ 1. Node.js accepts 10,000 requests.
12
+ 2. The handler runs 10,000 times.
13
+ 3. Your database receives 10,000 identical queries.
14
+ 4. **Result**: Database CPU spikes to 100%, latency skyrockets, and the site crashes.
15
+
16
+ ## The Solution: Request Fusion
17
+
18
+ With Request Fusion enabled, QHTTPX acts intelligently:
19
+
20
+ 1. **Request 1** arrives: QHTTPX marks it as the **Leader** and starts processing (querying the DB).
21
+ 2. **Requests 2 through 10,000** arrive *while Request 1 is still processing*.
22
+ 3. QHTTPX identifies them as **Followers** (identical semantic fingerprint).
23
+ 4. These requests **wait** for the Leader. They do *not* execute the handler.
24
+ 5. **Leader finishes**: The result is broadcast to all 9,999 Followers instantly.
25
+
26
+ **Result**:
27
+ - **1** Database Query.
28
+ - **10,000** Happy Users.
29
+ - **Zero** Database Load.
30
+
31
+ ## Enabling Fusion
32
+
33
+ Request Fusion is optional but recommended for read-heavy public APIs.
34
+
35
+ ### Using `createHttpApp` (Zero-Config)
36
+
37
+ ```typescript
38
+ const app = createHttpApp({
39
+ enableRequestFusion: true
40
+ });
41
+ ```
42
+
43
+ ### Manual Configuration
44
+
45
+ ```typescript
46
+ const app = new QHTTPX({
47
+ enableRequestFusion: true
48
+ });
49
+ ```
50
+
51
+ ## Semantic Hashing
52
+
53
+ How does QHTTPX know requests are identical? It generates a **Semantic Key** based on:
54
+
55
+ 1. **HTTP Method** (e.g., `GET`)
56
+ 2. **URL Path** (e.g., `/api/trending`)
57
+ 3. **Query Parameters** (sorted) (e.g., `?category=tech&page=1`)
58
+ 4. **Request Body** (if JSON)
59
+ 5. **Vary Headers** (Configurable, e.g., `Accept-Language`)
60
+
61
+ ## Micro-TTL
62
+
63
+ For even higher performance, Fusion includes a "Micro-TTL" mechanism. Even after a request finishes, the result can be held for a tiny window (e.g., 100ms) to satisfy immediately subsequent requests, acting as an ultra-short-term cache for burst traffic.
64
+
65
+ ## Best Practices
66
+
67
+ - **Idempotency**: Fusion is safest for `GET` requests. QHTTPX automatically disables Fusion for `POST`, `PUT`, `DELETE`, and `PATCH` by default to prevent side-effect duplication issues, though this can be overridden if you know what you are doing.
68
+ - **Personalized Content**: Be careful with endpoints that return user-specific data (like "My Profile"). If the response depends on the `Authorization` header, ensure that header is part of the Fusion Key (or disable Fusion for that route).
@@ -0,0 +1,65 @@
1
+ # Middleware
2
+
3
+ Middleware in QHTTPX functions similarly to Express or Koa but is optimized for the async scheduler. It allows you to execute code before or after the route handler.
4
+
5
+ ## Global Middleware
6
+
7
+ Register middleware using `app.use()`. It applies to all routes registered *after* it.
8
+
9
+ ```typescript
10
+ app.use(async (ctx, next) => {
11
+ const start = Date.now();
12
+
13
+ await next(); // Wait for downstream middleware/handler
14
+
15
+ const ms = Date.now() - start;
16
+ ctx.res.setHeader('X-Response-Time', `${ms}ms`);
17
+ });
18
+ ```
19
+
20
+ ## Destructuring Syntax
21
+
22
+ You can destructure the context arguments for cleaner code:
23
+
24
+ ```typescript
25
+ app.use(async ({ req, res }, next) => {
26
+ console.log(`${req.method} ${req.url}`);
27
+ await next();
28
+ });
29
+ ```
30
+
31
+ ## Built-in Middleware
32
+
33
+ QHTTPX comes with essential middleware optimized for its core.
34
+
35
+ ### Rate Limiting (Aegis)
36
+
37
+ See [Aegis Documentation](./AEGIS.md).
38
+
39
+ ### CORS
40
+
41
+ Enabled via `createHttpApp` or manually.
42
+
43
+ ```typescript
44
+ const app = createHttpApp({ cors: true });
45
+ ```
46
+
47
+ ### Compression
48
+
49
+ Automatically compresses responses using Gzip/Brotli.
50
+
51
+ ```typescript
52
+ const app = createHttpApp({ compression: true });
53
+ ```
54
+
55
+ ### Body Parser
56
+
57
+ The body parser is **built-in** and lazy-loaded. You do not need to `app.use(bodyParser())`.
58
+ Just access `ctx.body` and it will be parsed on demand.
59
+
60
+ ```typescript
61
+ app.post('/data', ({ body }) => {
62
+ // body is already parsed (JSON/URL-encoded)
63
+ return { received: body };
64
+ });
65
+ ```
@@ -0,0 +1,70 @@
1
+ # Routing
2
+
3
+ QHTTPX employs a high-performance **Radix Tree** (prefix tree) router. This data structure allows for `O(k)` route lookup time (where `k` is the length of the URL), making it significantly faster than the linear regex matching used by Express or Koa.
4
+
5
+ ## Basic Routing
6
+
7
+ Supported methods: `get`, `post`, `put`, `delete`, `patch`, `options`, `head`.
8
+
9
+ ```typescript
10
+ app.get('/', (ctx) => {
11
+ return ctx.json({ hello: 'world' });
12
+ });
13
+
14
+ app.post('/items', (ctx) => {
15
+ // ...
16
+ });
17
+ ```
18
+
19
+ ## Route Parameters
20
+
21
+ Capture dynamic segments of the URL using colons (`:`).
22
+
23
+ ```typescript
24
+ app.get('/users/:id', (ctx) => {
25
+ const { id } = ctx.params;
26
+ return ctx.json({ userId: id });
27
+ });
28
+
29
+ // Multiple parameters
30
+ app.get('/posts/:year/:month/:slug', (ctx) => {
31
+ const { year, month, slug } = ctx.params;
32
+ // ...
33
+ });
34
+ ```
35
+
36
+ ## Wildcards
37
+
38
+ Match everything following a path using `*`.
39
+
40
+ ```typescript
41
+ // Matches /files/image.png, /files/docs/report.pdf, etc.
42
+ app.get('/files/*', (ctx) => {
43
+ const path = ctx.params['*']; // "image.png"
44
+ // ...
45
+ });
46
+ ```
47
+
48
+ ## Route Destructuring
49
+
50
+ The handler context can be destructured for cleaner code:
51
+
52
+ ```typescript
53
+ app.get('/search', ({ query, json }) => {
54
+ const { q } = query;
55
+ return json({ results: performSearch(q) });
56
+ });
57
+ ```
58
+
59
+ ## Scoped Routes (Groups)
60
+
61
+ Use plugins to create route groups with prefixes (similar to `express.Router` or Fastify encapsulation).
62
+
63
+ ```typescript
64
+ const apiV1 = async (app: QHTTPX, opts: any) => {
65
+ app.get('/users', () => { ... }); // Becomes /api/v1/users
66
+ app.get('/posts', () => { ... }); // Becomes /api/v1/posts
67
+ };
68
+
69
+ app.register(apiV1, { prefix: '/api/v1' });
70
+ ```
package/docs/STATIC.md ADDED
@@ -0,0 +1,61 @@
1
+ # Static Files & Streaming
2
+
3
+ QHTTPX includes a high-performance static file server capable of handling media streaming (video/audio) and partial content requests out of the box.
4
+
5
+ ## Basic Usage
6
+
7
+ To serve static files (like images, CSS, JS) from a directory:
8
+
9
+ ```typescript
10
+ import path from 'path';
11
+
12
+ // Serve files from the 'public' folder at the root URL
13
+ app.static('/', path.join(__dirname, 'public'));
14
+
15
+ // Serve files from 'assets' under the '/static' prefix
16
+ app.static('/static', path.join(__dirname, 'assets'));
17
+ ```
18
+
19
+ ## Features
20
+
21
+ ### 1. Range Requests (Video Streaming)
22
+
23
+ The static handler automatically supports `Range` headers (HTTP 206 Partial Content). This means you can serve video files directly to browsers, and users can seek (jump) to different parts of the video without downloading the whole file.
24
+
25
+ ### 2. Smart Caching
26
+
27
+ It automatically handles:
28
+ - `Last-Modified` headers.
29
+ - `ETag` generation.
30
+ - `304 Not Modified` responses to save bandwidth.
31
+
32
+ ### 3. Streaming (Low Memory)
33
+
34
+ Files are streamed to the response using Node.js streams. This ensures that serving a large file (e.g., 1GB video) does not spike your server's RAM usage.
35
+
36
+ ## Configuration Options
37
+
38
+ You can customize the static handler:
39
+
40
+ ```typescript
41
+ app.static('/public', './public', {
42
+ maxAge: 86400, // Cache-Control max-age in seconds (default: 0)
43
+ index: 'index.html', // Default file for directories (default: index.html)
44
+ hidden: false // Serve hidden files starting with . (default: false)
45
+ });
46
+ ```
47
+
48
+ ## Manual Streaming
49
+
50
+ If you need to stream generated content or files manually within a route:
51
+
52
+ ```typescript
53
+ import fs from 'fs';
54
+
55
+ app.get('/download', ({ res }) => {
56
+ const stream = fs.createReadStream('./large-file.zip');
57
+
58
+ res.setHeader('Content-Type', 'application/zip');
59
+ stream.pipe(res);
60
+ });
61
+ ```
@@ -0,0 +1,76 @@
1
+ # WebSockets
2
+
3
+ QHTTPX includes a first-class, high-performance WebSocket implementation with built-in Pub/Sub capabilities. It handles protocol upgrades automatically within the same HTTP server instance.
4
+
5
+ ## Usage
6
+
7
+ ### 1. Enable WebSockets
8
+
9
+ No special configuration is needed. The WebSocket server is attached to the HTTP server automatically.
10
+
11
+ ### 2. Handle Upgrades
12
+
13
+ Use the `app.upgrade` method to handle the initial handshake.
14
+
15
+ ```typescript
16
+ app.upgrade('/ws', (req, socket, head) => {
17
+ // Perform authentication here if needed
18
+ // ...
19
+
20
+ // Accept the upgrade
21
+ app.websocket.handleUpgrade(req, socket, head, (ws) => {
22
+ app.websocket.emit('connection', ws, req);
23
+ });
24
+ });
25
+ ```
26
+
27
+ ### 3. Handle Connections
28
+
29
+ Listen for the `connection` event on the `app.websocket` instance.
30
+
31
+ ```typescript
32
+ app.websocket.on('connection', (ws, req) => {
33
+ console.log('New client connected!');
34
+
35
+ ws.on('message', (msg) => {
36
+ console.log('Received:', msg.toString());
37
+ ws.send(`Echo: ${msg}`);
38
+ });
39
+
40
+ ws.on('close', () => {
41
+ console.log('Client disconnected');
42
+ });
43
+ });
44
+ ```
45
+
46
+ ## Pub/Sub (Rooms)
47
+
48
+ QHTTPX allows you to broadcast messages to specific "rooms" or groups of clients.
49
+
50
+ ### Joining a Room
51
+
52
+ ```typescript
53
+ app.websocket.on('connection', (ws) => {
54
+ // Client joins 'chat-room-1'
55
+ ws.subscribe('chat-room-1');
56
+ });
57
+ ```
58
+
59
+ ### Broadcasting to a Room
60
+
61
+ You can broadcast from anywhere in your application (even outside the socket handler).
62
+
63
+ ```typescript
64
+ // Send to everyone in 'chat-room-1'
65
+ app.websocket.to('chat-room-1').emit('New Message!');
66
+ ```
67
+
68
+ ### Leaving a Room
69
+
70
+ ```typescript
71
+ ws.unsubscribe('chat-room-1');
72
+ ```
73
+
74
+ ## Performance Note
75
+
76
+ The WebSocket implementation shares the same efficient async scheduler as the HTTP layer, allowing thousands of concurrent connections with minimal overhead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qhttpx",
3
- "version": "1.8.5",
3
+ "version": "1.8.11",
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",
@@ -14,6 +14,15 @@
14
14
  "bin": {
15
15
  "qhttpx": "./dist/src/cli/index.js"
16
16
  },
17
+ "files": [
18
+ "dist",
19
+ "src/native",
20
+ "binding.gyp",
21
+ "README.md",
22
+ "LICENSE",
23
+ "CHANGELOG.md",
24
+ "docs"
25
+ ],
17
26
  "directories": {
18
27
  "doc": "docs"
19
28
  },
@@ -82,6 +91,7 @@
82
91
  "better-sqlite3": "^12.6.2",
83
92
  "busboy": "^1.6.0",
84
93
  "fast-json-stringify": "^5.15.1",
94
+ "node-addon-api": "^8.5.0",
85
95
  "pino": "^10.2.0",
86
96
  "pino-pretty": "^13.1.3",
87
97
  "quantam-async": "^0.1.1",
@@ -0,0 +1,31 @@
1
+ # QHTTPX Native Addon
2
+
3
+ This directory contains the C++ native addon for QHTTPX to accelerate HTTP parsing and response generation.
4
+
5
+ ## Prerequisites
6
+
7
+ To build this addon, you need:
8
+ - **Windows**: Visual Studio Build Tools (Desktop development with C++).
9
+ - **Linux/macOS**: Python 3, make, and a C++ compiler (GCC/Clang).
10
+
11
+ ## Building
12
+
13
+ The build is handled automatically by `npm install` if `node-gyp` and build tools are present.
14
+
15
+ To manually rebuild:
16
+ ```bash
17
+ npx node-gyp rebuild
18
+ ```
19
+
20
+ ## Structure
21
+
22
+ - `addon.cc`: Entry point, registers the module.
23
+ - `server.cc`: `NativeServer` class implementation.
24
+ - `picohttpparser.c/h`: The HTTP parser library (vendored).
25
+ - `index.ts`: TypeScript wrapper and fallback logic.
26
+
27
+ ## Usage
28
+
29
+ The `NativeAdapter` in `src/core/native-adapter.ts` automatically detects if the native addon is available.
30
+ If available, it uses `net.Server` + `NativeServer` for handling requests.
31
+ If not, it falls back to the standard `http.Server`.
@@ -0,0 +1,8 @@
1
+ #include <napi.h>
2
+ #include "server.h"
3
+
4
+ Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
5
+ return NativeServer::Init(env, exports);
6
+ }
7
+
8
+ NODE_API_MODULE(qhttpx_native, InitAll)
@@ -0,0 +1,78 @@
1
+
2
+
3
+ export interface NativeServerBinding {
4
+ parse(buffer: Buffer): {
5
+ method: string;
6
+ path: string;
7
+ version: number;
8
+ headers: Record<string, string>;
9
+ bodyOffset: number;
10
+ } | null;
11
+ createResponse(statusCode: number, headers: Record<string, string>, body?: string | Buffer): Buffer;
12
+ createJSONResponse(obj: unknown): Buffer;
13
+ writeResponse(fd: number, chunks: (Buffer | string)[]): void;
14
+ setCPUAffinity(cpuId: number): boolean;
15
+ }
16
+
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ let nativeBinding: any = null;
19
+
20
+ // Use try/catch with module.createRequire if import.meta is not available in some build targets
21
+ // However, since we are in a TS module that might be commonjs or esm
22
+ // We'll use a safer approach for require
23
+ try {
24
+ const req = require;
25
+
26
+ try {
27
+ nativeBinding = req('../../build/Release/qhttpx_native.node');
28
+ } catch {
29
+ try {
30
+ nativeBinding = req('../../build/Debug/qhttpx_native.node');
31
+ } catch {
32
+ // Ignored
33
+ }
34
+ }
35
+ } catch {
36
+ // Ignored
37
+ }
38
+
39
+ export class NativeServer {
40
+ private binding: NativeServerBinding | null;
41
+
42
+ constructor() {
43
+ if (nativeBinding && nativeBinding.NativeServer) {
44
+ this.binding = new nativeBinding.NativeServer();
45
+ } else {
46
+ this.binding = null;
47
+ }
48
+ }
49
+
50
+ public get isAvailable(): boolean {
51
+ return this.binding !== null;
52
+ }
53
+
54
+ public parse(buffer: Buffer) {
55
+ if (!this.binding) throw new Error('Native bindings not available');
56
+ return this.binding.parse(buffer);
57
+ }
58
+
59
+ public createResponse(statusCode: number, headers: Record<string, string>, body?: string | Buffer) {
60
+ if (!this.binding) throw new Error('Native bindings not available');
61
+ return this.binding.createResponse(statusCode, headers, body);
62
+ }
63
+
64
+ public createJSONResponse(obj: unknown) {
65
+ if (!this.binding) throw new Error('Native bindings not available');
66
+ return this.binding.createJSONResponse(obj);
67
+ }
68
+
69
+ public writeResponse(fd: number, chunks: (Buffer | string)[]) {
70
+ if (!this.binding) throw new Error('Native bindings not available');
71
+ return this.binding.writeResponse(fd, chunks);
72
+ }
73
+
74
+ public setCPUAffinity(cpuId: number) {
75
+ if (!this.binding) throw new Error('Native bindings not available');
76
+ return this.binding.setCPUAffinity(cpuId);
77
+ }
78
+ }