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
@@ -0,0 +1,262 @@
1
+ #include "server.h"
2
+ #include "picohttpparser.h"
3
+ #include <iostream>
4
+
5
+ #ifdef _WIN32
6
+ #include <io.h>
7
+ #include <windows.h>
8
+ #else
9
+ #include <unistd.h>
10
+ #include <sys/uio.h>
11
+ #include <sched.h>
12
+ #include <pthread.h>
13
+ #endif
14
+
15
+ Napi::Object NativeServer::Init(Napi::Env env, Napi::Object exports) {
16
+ Napi::Function func = DefineClass(env, "NativeServer", {
17
+ InstanceMethod("parse", &NativeServer::Parse),
18
+ InstanceMethod("createResponse", &NativeServer::CreateResponse),
19
+ InstanceMethod("createJSONResponse", &NativeServer::CreateJSONResponse),
20
+ InstanceMethod("writeResponse", &NativeServer::WriteResponse),
21
+ InstanceMethod("setCPUAffinity", &NativeServer::SetCPUAffinity),
22
+ });
23
+
24
+ Napi::FunctionReference* constructor = new Napi::FunctionReference();
25
+ *constructor = Napi::Persistent(func);
26
+ env.SetInstanceData(constructor);
27
+
28
+ exports.Set("NativeServer", func);
29
+ return exports;
30
+ }
31
+
32
+ NativeServer::NativeServer(const Napi::CallbackInfo& info) : Napi::ObjectWrap<NativeServer>(info) {
33
+ // Constructor logic if needed
34
+ }
35
+
36
+ // Parses HTTP request buffer
37
+ // Returns object: { method, path, headers, version, bodyOffset }
38
+ Napi::Value NativeServer::Parse(const Napi::CallbackInfo& info) {
39
+ Napi::Env env = info.Env();
40
+
41
+ if (info.Length() < 1 || !info[0].IsBuffer()) {
42
+ Napi::TypeError::New(env, "Buffer expected").ThrowAsJavaScriptException();
43
+ return env.Null();
44
+ }
45
+
46
+ Napi::Buffer<char> buffer = info[0].As<Napi::Buffer<char>>();
47
+ const char* buf = buffer.Data();
48
+ size_t len = buffer.Length();
49
+
50
+ const char *method, *path;
51
+ size_t method_len, path_len;
52
+ int minor_version;
53
+ struct phr_header headers[100];
54
+ size_t num_headers = sizeof(headers) / sizeof(headers[0]);
55
+
56
+ int ret = phr_parse_request(buf, len, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, 0);
57
+
58
+ if (ret > 0) {
59
+ Napi::Object result = Napi::Object::New(env);
60
+ result.Set("method", Napi::String::New(env, method, method_len));
61
+ result.Set("path", Napi::String::New(env, path, path_len));
62
+ result.Set("version", Napi::Number::New(env, minor_version));
63
+ result.Set("bodyOffset", Napi::Number::New(env, ret));
64
+
65
+ Napi::Object headersObj = Napi::Object::New(env);
66
+ for (size_t i = 0; i < num_headers; ++i) {
67
+ // Lowercase header names? Typically yes for Node.js compat
68
+ // For now, we just pass raw. Optimization: In C++ we could lowercase here.
69
+ std::string name(headers[i].name, headers[i].name_len);
70
+ // Simple lowercase
71
+ for(auto& c : name) c = tolower(c);
72
+
73
+ Napi::String key = Napi::String::New(env, name);
74
+ Napi::String value = Napi::String::New(env, headers[i].value, headers[i].value_len);
75
+ headersObj.Set(key, value);
76
+ }
77
+ result.Set("headers", headersObj);
78
+
79
+ return result;
80
+ } else if (ret == -1) {
81
+ // Parse error
82
+ return env.Null();
83
+ } else {
84
+ // Partial (ret == -2)
85
+ return env.Undefined();
86
+ }
87
+ }
88
+
89
+ // Fast Response Builder
90
+ // createResponse(statusCode, headers, body) -> Buffer
91
+ Napi::Value NativeServer::CreateResponse(const Napi::CallbackInfo& info) {
92
+ Napi::Env env = info.Env();
93
+
94
+ int statusCode = 200;
95
+ if (info.Length() > 0 && info[0].IsNumber()) {
96
+ statusCode = info[0].As<Napi::Number>().Int32Value();
97
+ }
98
+
99
+ std::string statusMsg = "OK";
100
+ if (statusCode == 404) statusMsg = "Not Found";
101
+ else if (statusCode == 500) statusMsg = "Internal Server Error";
102
+
103
+ std::string response;
104
+ response.reserve(1024);
105
+
106
+ response += "HTTP/1.1 " + std::to_string(statusCode) + " " + statusMsg + "\r\n";
107
+ response += "Connection: keep-alive\r\n";
108
+ response += "Date: Mon, 27 Jul 2029 12:28:53 GMT\r\n"; // Mock date for speed, real impl needs date cache
109
+
110
+ if (info.Length() > 1 && info[1].IsObject()) {
111
+ Napi::Object headers = info[1].As<Napi::Object>();
112
+ Napi::Array props = headers.GetPropertyNames();
113
+ uint32_t len = props.Length();
114
+ for (uint32_t i = 0; i < len; ++i) {
115
+ Napi::Value key = props.Get(i);
116
+ Napi::Value val = headers.Get(key);
117
+ response += key.ToString().Utf8Value() + ": " + val.ToString().Utf8Value() + "\r\n";
118
+ }
119
+ }
120
+
121
+ std::string body = "";
122
+ if (info.Length() > 2) {
123
+ if (info[2].IsString()) {
124
+ body = info[2].As<Napi::String>().Utf8Value();
125
+ } else if (info[2].IsBuffer()) {
126
+ Napi::Buffer<char> b = info[2].As<Napi::Buffer<char>>();
127
+ body.assign(b.Data(), b.Length());
128
+ }
129
+ }
130
+
131
+ response += "Content-Length: " + std::to_string(body.length()) + "\r\n";
132
+ response += "\r\n";
133
+ response += body;
134
+
135
+ return Napi::Buffer<char>::Copy(env, response.data(), response.length());
136
+ }
137
+
138
+ // createJSONResponse(obj) -> Buffer
139
+ Napi::Value NativeServer::CreateJSONResponse(const Napi::CallbackInfo& info) {
140
+ Napi::Env env = info.Env();
141
+
142
+ if (info.Length() < 1) {
143
+ Napi::TypeError::New(env, "Object expected").ThrowAsJavaScriptException();
144
+ return env.Null();
145
+ }
146
+
147
+ // 1. Serialize to JSON string
148
+ Napi::Object json = env.Global().Get("JSON").As<Napi::Object>();
149
+ Napi::Function stringify = json.Get("stringify").As<Napi::Function>();
150
+ Napi::String jsonStr = stringify.Call({ info[0] }).As<Napi::String>();
151
+ std::string body = jsonStr.Utf8Value();
152
+
153
+ // 2. Wrap in HTTP
154
+ std::string response;
155
+ response.reserve(body.length() + 128);
156
+
157
+ // Minimal headers for speed
158
+ response += "HTTP/1.1 200 OK\r\n";
159
+ response += "Content-Type: application/json\r\n";
160
+ response += "Content-Length: " + std::to_string(body.length()) + "\r\n";
161
+ response += "Date: Mon, 27 Jul 2029 12:28:53 GMT\r\n";
162
+ response += "Connection: keep-alive\r\n\r\n";
163
+ response += body;
164
+
165
+ return Napi::Buffer<char>::Copy(env, response.data(), response.length());
166
+ }
167
+
168
+ // writeResponse(fd, iovecs[]) -> void
169
+ Napi::Value NativeServer::WriteResponse(const Napi::CallbackInfo& info) {
170
+ Napi::Env env = info.Env();
171
+
172
+ if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsArray()) {
173
+ Napi::TypeError::New(env, "FD (number) and iovecs (array) expected").ThrowAsJavaScriptException();
174
+ return env.Null();
175
+ }
176
+
177
+ int fd = info[0].As<Napi::Number>().Int32Value();
178
+ Napi::Array chunks = info[1].As<Napi::Array>();
179
+ uint32_t len = chunks.Length();
180
+
181
+ #ifdef _WIN32
182
+ // Windows implementation using write() loop for now
183
+ // Since WSASend requires WSA structures, keeping it simple for "ABI lock"
184
+ for (uint32_t i = 0; i < len; i++) {
185
+ Napi::Value val = chunks.Get(i);
186
+ if (val.IsBuffer()) {
187
+ Napi::Buffer<char> buf = val.As<Napi::Buffer<char>>();
188
+ _write(fd, buf.Data(), buf.Length());
189
+ } else if (val.IsString()) {
190
+ std::string str = val.As<Napi::String>().Utf8Value();
191
+ _write(fd, str.data(), str.length());
192
+ }
193
+ }
194
+ #else
195
+ // Linux/Unix implementation using writev if possible, or loop
196
+ // To properly use writev, we need to convert Napi::Array to struct iovec[]
197
+ // For large arrays, dynamic allocation is needed.
198
+
199
+ std::vector<struct iovec> iov;
200
+ iov.reserve(len);
201
+
202
+ // Keep buffers alive? No, Napi handles are valid during call.
203
+ // But we need to make sure we don't copy if possible.
204
+ // Napi::Buffer::Data returns pointer.
205
+
206
+ for (uint32_t i = 0; i < len; i++) {
207
+ Napi::Value val = chunks.Get(i);
208
+ if (val.IsBuffer()) {
209
+ Napi::Buffer<char> buf = val.As<Napi::Buffer<char>>();
210
+ struct iovec v;
211
+ v.iov_base = buf.Data();
212
+ v.iov_len = buf.Length();
213
+ iov.push_back(v);
214
+ }
215
+ }
216
+
217
+ if (!iov.empty()) {
218
+ ssize_t written = writev(fd, iov.data(), iov.size());
219
+ if (written < 0) {
220
+ // TODO: Better error handling for writev failure
221
+ }
222
+ }
223
+ #endif
224
+
225
+ return env.Undefined();
226
+ }
227
+
228
+ // setCPUAffinity(cpuId) -> boolean
229
+ Napi::Value NativeServer::SetCPUAffinity(const Napi::CallbackInfo& info) {
230
+ Napi::Env env = info.Env();
231
+ if (info.Length() < 1 || !info[0].IsNumber()) {
232
+ Napi::TypeError::New(env, "CPU ID (number) expected").ThrowAsJavaScriptException();
233
+ return env.Null();
234
+ }
235
+
236
+ int cpuId = info[0].As<Napi::Number>().Int32Value();
237
+
238
+ #ifdef _WIN32
239
+ HANDLE process = GetCurrentProcess();
240
+ DWORD_PTR mask = (DWORD_PTR)1 << cpuId;
241
+ BOOL res = SetProcessAffinityMask(process, mask);
242
+ return Napi::Boolean::New(env, res != 0);
243
+ #else
244
+ // Linux/Unix implementation
245
+ // Note: sched_setaffinity is Linux specific.
246
+ // For macOS/BSD, it might be different (thread_policy_set), but we target Linux for production usually.
247
+ // Using pthread_setaffinity_np if available (common in Linux pthreads)
248
+
249
+ #if defined(__linux__)
250
+ cpu_set_t cpuset;
251
+ CPU_ZERO(&cpuset);
252
+ CPU_SET(cpuId, &cpuset);
253
+ pthread_t thread = pthread_self();
254
+ int res = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
255
+ return Napi::Boolean::New(env, res == 0);
256
+ #else
257
+ // Not implemented for this OS (e.g. macOS)
258
+ return Napi::Boolean::New(env, false);
259
+ #endif
260
+
261
+ #endif
262
+ }
@@ -0,0 +1,30 @@
1
+ #ifndef NATIVE_SERVER_H
2
+ #define NATIVE_SERVER_H
3
+
4
+ #include <napi.h>
5
+ #include <vector>
6
+ #include <string>
7
+
8
+ class NativeServer : public Napi::ObjectWrap<NativeServer> {
9
+ public:
10
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
11
+ NativeServer(const Napi::CallbackInfo& info);
12
+
13
+ private:
14
+ Napi::Value Parse(const Napi::CallbackInfo& info);
15
+ Napi::Value SerializeResponse(const Napi::CallbackInfo& info);
16
+
17
+ // Helper to create a response buffer directly
18
+ Napi::Value CreateResponse(const Napi::CallbackInfo& info);
19
+
20
+ // Zero-copy JSON fast path
21
+ Napi::Value CreateJSONResponse(const Napi::CallbackInfo& info);
22
+
23
+ // Direct socket write (writev wrapper)
24
+ Napi::Value WriteResponse(const Napi::CallbackInfo& info);
25
+
26
+ // CPU Affinity
27
+ Napi::Value SetCPUAffinity(const Napi::CallbackInfo& info);
28
+ };
29
+
30
+ #endif
package/.eslintrc.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "root": true,
3
- "env": {
4
- "es2020": true,
5
- "node": true
6
- },
7
- "parser": "@typescript-eslint/parser",
8
- "parserOptions": {
9
- "project": "./tsconfig.json",
10
- "sourceType": "module"
11
- },
12
- "plugins": ["@typescript-eslint"],
13
- "extends": [
14
- "eslint:recommended",
15
- "plugin:@typescript-eslint/recommended",
16
- "plugin:prettier/recommended"
17
- ],
18
- "rules": {
19
- "@typescript-eslint/no-explicit-any": "off"
20
- },
21
- "ignorePatterns": ["dist/**", "node_modules/**"]
22
- }
@@ -1,32 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
- - master
8
- pull_request:
9
-
10
- jobs:
11
- build-and-test:
12
- runs-on: ubuntu-latest
13
-
14
- steps:
15
- - name: Checkout
16
- uses: actions/checkout@v4
17
-
18
- - name: Setup Node.js
19
- uses: actions/setup-node@v4
20
- with:
21
- node-version: 20
22
- cache: "npm"
23
-
24
- - name: Install dependencies
25
- run: npm ci
26
-
27
- - name: Lint
28
- run: npm run lint
29
-
30
- - name: Test
31
- run: npm test
32
-
@@ -1,37 +0,0 @@
1
- name: Publish to NPM
2
-
3
- on:
4
- release:
5
- types: [published]
6
-
7
- jobs:
8
- publish:
9
- runs-on: ubuntu-latest
10
- permissions:
11
- contents: read
12
- id-token: write
13
-
14
- steps:
15
- - name: Checkout
16
- uses: actions/checkout@v4
17
-
18
- - name: Setup Node.js
19
- uses: actions/setup-node@v4
20
- with:
21
- node-version: 20
22
- registry-url: 'https://registry.npmjs.org'
23
- cache: 'npm'
24
-
25
- - name: Install dependencies
26
- run: npm ci
27
-
28
- - name: Build
29
- run: npm run build
30
-
31
- - name: Test
32
- run: npm test
33
-
34
- - name: Publish to NPM
35
- run: npm publish --access public
36
- env:
37
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,21 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- tags:
6
- - "v*"
7
-
8
- permissions:
9
- contents: write
10
-
11
- jobs:
12
- release:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - name: Checkout
16
- uses: actions/checkout@v4
17
-
18
- - name: Create Release
19
- uses: softprops/action-gh-release@v1
20
- with:
21
- generate_release_notes: true
package/.prettierrc DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "singleQuote": true,
3
- "semi": true,
4
- "trailingComma": "all",
5
- "printWidth": 80,
6
- "endOfLine": "auto"
7
- }
package/assets/logo.svg DELETED
@@ -1,25 +0,0 @@
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>
25
- </svg>
package/eslint.config.cjs DELETED
@@ -1,26 +0,0 @@
1
- const tsParser = require("@typescript-eslint/parser");
2
- const tsPlugin = require("@typescript-eslint/eslint-plugin");
3
- const prettierPlugin = require("eslint-plugin-prettier");
4
-
5
- module.exports = [
6
- {
7
- files: ["**/*.ts"],
8
- ignores: ["dist/**", "node_modules/**"],
9
- languageOptions: {
10
- parser: tsParser,
11
- parserOptions: {
12
- project: "./tsconfig.json",
13
- tsconfigRootDir: __dirname,
14
- sourceType: "module",
15
- },
16
- },
17
- plugins: {
18
- "@typescript-eslint": tsPlugin,
19
- prettier: prettierPlugin,
20
- },
21
- rules: {
22
- ...tsPlugin.configs.recommended.rules,
23
- "prettier/prettier": "off",
24
- },
25
- },
26
- ];
@@ -1,62 +0,0 @@
1
- import { createHttpApp } from '../src/index';
2
-
3
- // 1. Initialize App (Fusion + Aegis enabled)
4
- const app = createHttpApp({
5
- enableRequestFusion: true, // ⚡ Auto-coalesce duplicate requests
6
- metricsEnabled: true // 📊 Expose /__qhttpx/metrics
7
- });
8
-
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();
13
- });
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
-
24
- // 4. Routes (Clean & Destructured)
25
- app.get('/', ({ json }) => json({ status: 'online', fusion: true }));
26
-
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() });
31
- });
32
-
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);
37
- });
38
-
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 });
44
- });
45
-
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
- });
53
- });
54
-
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);
59
- });
60
-
61
- // 6. Start Server
62
- app.listen(3000, () => console.log('🚀 Server running on http://localhost:3000'));
@@ -1,70 +0,0 @@
1
- import { quantam } from 'quantam-async';
2
- import { QHTTPX } from '../index';
3
-
4
- async function run() {
5
- const app = new QHTTPX({
6
- maxConcurrency: 512,
7
- requestTimeoutMs: 10_000,
8
- });
9
-
10
- app.get('/json', (ctx) => {
11
- ctx.json({ message: 'hello from qhttpx' });
12
- });
13
-
14
- const { port } = await app.listen(0, '127.0.0.1');
15
-
16
- const url = `http://127.0.0.1:${port}/json`;
17
-
18
- const requestsPerUser = 10;
19
- const userCount = 10_000;
20
- const maxConcurrentUsers = 1_000;
21
-
22
- const flow = quantam<number>()
23
- .name('user-flow')
24
- .step(async (userId) => {
25
- for (let i = 0; i < requestsPerUser; i += 1) {
26
- const response = await fetch(url);
27
- if (!response.ok) {
28
- throw new Error(
29
- `user ${userId} request ${i} failed with status ${response.status}`,
30
- );
31
- }
32
- await response.text();
33
- }
34
- return userId;
35
- })
36
- .retry(3, 50)
37
- .timeout(30_000);
38
-
39
- const inputs: number[] = [];
40
- for (let i = 0; i < userCount; i += 1) {
41
- inputs.push(i);
42
- }
43
-
44
- const start = Date.now();
45
- try {
46
- await flow.runMany(inputs, {
47
- concurrency: maxConcurrentUsers,
48
- });
49
- } catch (err) {
50
- console.error('Quantam runMany error', err);
51
- }
52
- const durationSec = (Date.now() - start) / 1000;
53
- const totalRequests = userCount * requestsPerUser;
54
- const rps = totalRequests / durationSec;
55
-
56
- console.log(
57
- `Quantam bench: users=${userCount}, requestsPerUser=${requestsPerUser}, ` +
58
- `totalRequests=${totalRequests}, duration=${durationSec.toFixed(
59
- 2,
60
- )}s, rps=${rps.toFixed(0)}, concurrency=${maxConcurrentUsers}`,
61
- );
62
-
63
- await app.close();
64
- process.exit(0);
65
- }
66
-
67
- run().catch((err) => {
68
- console.error(err);
69
- process.exit(1);
70
- });
@@ -1,71 +0,0 @@
1
- import autocannon from 'autocannon';
2
- import { QHTTPX } from '../index';
3
-
4
- function runAutocannon(url: string): Promise<autocannon.Result> {
5
- return new Promise((resolve, reject) => {
6
- const instance = autocannon(
7
- {
8
- url,
9
- amount: 10_000,
10
- connections: 10_000,
11
- pipelining: 1,
12
- duration: 10,
13
- },
14
- (err, result) => {
15
- if (err) {
16
- reject(err);
17
- return;
18
- }
19
- resolve(result);
20
- },
21
- );
22
-
23
- instance.on('error', (err) => {
24
- reject(err);
25
- });
26
- });
27
- }
28
-
29
- async function run() {
30
- const payloadBuffer = Buffer.from(
31
- JSON.stringify({ message: 'hello from qhttpx' }),
32
- );
33
- const app = new QHTTPX({
34
- maxConcurrency: 512,
35
- metricsEnabled: false,
36
- jsonSerializer: () => payloadBuffer,
37
- });
38
-
39
- app.get('/json', (ctx) => {
40
- ctx.json({ message: 'hello from qhttpx' });
41
- });
42
-
43
- const { port } = await app.listen(0, '127.0.0.1');
44
-
45
- const url = `http://127.0.0.1:${port}/json`;
46
-
47
- const result = await runAutocannon(url);
48
-
49
- autocannon.printResult(result);
50
-
51
- const totalRequests = result.requests.total;
52
- const sent = result.requests.sent;
53
- const avgRps = result.requests.average.toFixed(0);
54
- const p99 = result.latency.p99.toFixed(1);
55
- const connections = result.connections;
56
- const pipelining = result.pipelining;
57
- // Summary line that shows up clearly in CI/dev logs
58
- console.log(
59
- `QHTTPX bench: total=${totalRequests} (sent=${sent}) req, ` +
60
- `${avgRps} req/sec, p99=${p99}ms, connections=${connections}, ` +
61
- `pipelining=${pipelining}`,
62
- );
63
-
64
- await app.close();
65
- process.exit(0);
66
- }
67
-
68
- run().catch((err) => {
69
- console.error(err);
70
- process.exit(1);
71
- });