qhttpx 1.9.2 → 1.9.3

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 (232) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +79 -17
  3. package/dist/examples/api-server.d.ts +1 -0
  4. package/dist/examples/api-server.js +77 -0
  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 +112 -0
  24. package/dist/src/benchmarks/compare-frameworks.js +119 -0
  25. package/dist/src/benchmarks/compare.d.ts +1 -0
  26. package/dist/src/benchmarks/compare.js +288 -0
  27. package/dist/src/benchmarks/quantam-users.d.ts +1 -0
  28. package/dist/src/benchmarks/quantam-users.js +56 -0
  29. package/dist/src/benchmarks/simple-json.d.ts +1 -0
  30. package/dist/src/benchmarks/simple-json.js +60 -0
  31. package/dist/src/benchmarks/ultra-mode.d.ts +1 -0
  32. package/dist/src/benchmarks/ultra-mode.js +94 -0
  33. package/dist/src/buffer-pool.js +70 -0
  34. package/dist/src/cli/index.d.ts +2 -0
  35. package/dist/src/cli/index.js +222 -0
  36. package/dist/src/client/index.d.ts +17 -0
  37. package/dist/src/client/index.js +72 -0
  38. package/dist/src/config.js +50 -0
  39. package/dist/src/cookies.js +59 -0
  40. package/dist/src/core/batch.d.ts +24 -0
  41. package/dist/src/core/batch.js +97 -0
  42. package/dist/src/core/body-parser.d.ts +15 -0
  43. package/dist/src/core/body-parser.js +121 -0
  44. package/dist/src/core/buffer-pool.d.ts +41 -0
  45. package/dist/src/core/buffer-pool.js +70 -0
  46. package/dist/src/core/config.d.ts +7 -0
  47. package/dist/src/core/config.js +50 -0
  48. package/dist/src/core/errors.d.ts +34 -0
  49. package/dist/src/core/errors.js +70 -0
  50. package/dist/src/core/fusion.d.ts +20 -0
  51. package/dist/src/core/fusion.js +193 -0
  52. package/dist/src/core/logger.d.ts +22 -0
  53. package/dist/src/core/logger.js +49 -0
  54. package/dist/src/core/metrics.d.ts +48 -0
  55. package/dist/src/core/metrics.js +117 -0
  56. package/dist/src/core/native-adapter.d.ts +11 -0
  57. package/dist/src/core/native-adapter.js +211 -0
  58. package/dist/src/core/resources.d.ts +9 -0
  59. package/dist/src/core/resources.js +25 -0
  60. package/dist/src/core/scheduler.d.ts +34 -0
  61. package/dist/src/core/scheduler.js +85 -0
  62. package/dist/src/core/scope.d.ts +26 -0
  63. package/dist/src/core/scope.js +68 -0
  64. package/dist/src/core/serializer.d.ts +10 -0
  65. package/dist/src/core/serializer.js +44 -0
  66. package/dist/src/core/server.d.ts +138 -0
  67. package/dist/src/core/server.js +1082 -0
  68. package/dist/src/core/stream.d.ts +15 -0
  69. package/dist/src/core/stream.js +71 -0
  70. package/dist/src/core/tasks.d.ts +29 -0
  71. package/dist/src/core/tasks.js +87 -0
  72. package/dist/src/core/types.d.ts +173 -0
  73. package/dist/src/core/types.js +19 -0
  74. package/dist/src/core/websocket.d.ts +25 -0
  75. package/dist/src/core/websocket.js +86 -0
  76. package/dist/src/core/worker-queue.d.ts +41 -0
  77. package/dist/src/core/worker-queue.js +73 -0
  78. package/dist/src/cors.js +66 -0
  79. package/dist/src/database/adapters/memory.d.ts +21 -0
  80. package/dist/src/database/adapters/memory.js +90 -0
  81. package/dist/src/database/adapters/mongo.d.ts +11 -0
  82. package/dist/src/database/adapters/mongo.js +141 -0
  83. package/dist/src/database/adapters/postgres.d.ts +10 -0
  84. package/dist/src/database/adapters/postgres.js +111 -0
  85. package/dist/src/database/adapters/sqlite.d.ts +10 -0
  86. package/dist/src/database/adapters/sqlite.js +42 -0
  87. package/dist/src/database/coalescer.d.ts +14 -0
  88. package/dist/src/database/coalescer.js +134 -0
  89. package/dist/src/database/manager.d.ts +35 -0
  90. package/dist/src/database/manager.js +87 -0
  91. package/dist/src/database/types.d.ts +20 -0
  92. package/dist/src/database/types.js +2 -0
  93. package/dist/src/index.d.ts +50 -0
  94. package/dist/src/index.js +91 -0
  95. package/dist/src/logger.js +45 -0
  96. package/dist/src/metrics.js +111 -0
  97. package/dist/src/middleware/compression.d.ts +2 -0
  98. package/dist/src/middleware/compression.js +133 -0
  99. package/dist/src/middleware/cors.d.ts +2 -0
  100. package/dist/src/middleware/cors.js +66 -0
  101. package/dist/src/middleware/presets.d.ts +16 -0
  102. package/dist/src/middleware/presets.js +52 -0
  103. package/dist/src/middleware/rate-limit.d.ts +14 -0
  104. package/dist/src/middleware/rate-limit.js +83 -0
  105. package/dist/src/middleware/security.d.ts +21 -0
  106. package/dist/src/middleware/security.js +69 -0
  107. package/dist/src/middleware/static.d.ts +11 -0
  108. package/dist/src/middleware/static.js +191 -0
  109. package/dist/src/native/index.d.ts +32 -0
  110. package/dist/src/native/index.js +141 -0
  111. package/dist/src/openapi/generator.d.ts +19 -0
  112. package/dist/src/openapi/generator.js +149 -0
  113. package/dist/src/presets.js +33 -0
  114. package/dist/src/radix-router.js +89 -0
  115. package/dist/src/radix-tree.js +81 -0
  116. package/dist/src/resources.js +25 -0
  117. package/dist/src/router/radix-router.d.ts +18 -0
  118. package/dist/src/router/radix-router.js +89 -0
  119. package/dist/src/router/radix-tree.d.ts +18 -0
  120. package/dist/src/router/radix-tree.js +131 -0
  121. package/dist/src/router/router.d.ts +34 -0
  122. package/dist/src/router/router.js +186 -0
  123. package/dist/src/router.js +138 -0
  124. package/dist/src/scheduler.js +85 -0
  125. package/dist/src/security.js +69 -0
  126. package/dist/src/server.js +685 -0
  127. package/dist/src/signals.js +31 -0
  128. package/dist/src/static.js +107 -0
  129. package/dist/src/stream.js +71 -0
  130. package/dist/src/tasks.js +87 -0
  131. package/dist/src/testing/index.d.ts +25 -0
  132. package/dist/src/testing/index.js +84 -0
  133. package/dist/src/testing.js +40 -0
  134. package/dist/src/types.js +19 -0
  135. package/dist/src/utils/cookies.d.ts +3 -0
  136. package/dist/src/utils/cookies.js +59 -0
  137. package/dist/src/utils/logger.d.ts +12 -0
  138. package/dist/src/utils/logger.js +45 -0
  139. package/dist/src/utils/signals.d.ts +6 -0
  140. package/dist/src/utils/signals.js +31 -0
  141. package/dist/src/utils/sse.d.ts +6 -0
  142. package/dist/src/utils/sse.js +32 -0
  143. package/dist/src/utils/testing.js +40 -0
  144. package/dist/src/validation/index.d.ts +3 -0
  145. package/dist/src/validation/index.js +19 -0
  146. package/dist/src/validation/simple.d.ts +5 -0
  147. package/dist/src/validation/simple.js +102 -0
  148. package/dist/src/validation/types.d.ts +32 -0
  149. package/dist/src/validation/types.js +12 -0
  150. package/dist/src/validation/zod.d.ts +4 -0
  151. package/dist/src/validation/zod.js +18 -0
  152. package/dist/src/views/index.d.ts +1 -0
  153. package/dist/src/views/index.js +17 -0
  154. package/dist/src/views/types.d.ts +3 -0
  155. package/dist/src/views/types.js +2 -0
  156. package/dist/src/worker-queue.js +73 -0
  157. package/dist/tests/adapters.test.d.ts +1 -0
  158. package/dist/tests/adapters.test.js +106 -0
  159. package/dist/tests/batch.test.d.ts +1 -0
  160. package/dist/tests/batch.test.js +117 -0
  161. package/dist/tests/body-parser.test.d.ts +1 -0
  162. package/dist/tests/body-parser.test.js +52 -0
  163. package/dist/tests/compression-sse.test.d.ts +1 -0
  164. package/dist/tests/compression-sse.test.js +87 -0
  165. package/dist/tests/cookies.test.d.ts +1 -0
  166. package/dist/tests/cookies.test.js +63 -0
  167. package/dist/tests/cors.test.d.ts +1 -0
  168. package/dist/tests/cors.test.js +55 -0
  169. package/dist/tests/database.test.d.ts +1 -0
  170. package/dist/tests/database.test.js +80 -0
  171. package/dist/tests/dx.test.d.ts +1 -0
  172. package/dist/tests/dx.test.js +114 -0
  173. package/dist/tests/ecosystem.test.d.ts +1 -0
  174. package/dist/tests/ecosystem.test.js +133 -0
  175. package/dist/tests/features.test.d.ts +1 -0
  176. package/dist/tests/features.test.js +47 -0
  177. package/dist/tests/fusion.test.d.ts +1 -0
  178. package/dist/tests/fusion.test.js +92 -0
  179. package/dist/tests/http-basic.test.d.ts +1 -0
  180. package/dist/tests/http-basic.test.js +124 -0
  181. package/dist/tests/logger.test.d.ts +1 -0
  182. package/dist/tests/logger.test.js +33 -0
  183. package/dist/tests/middleware.test.d.ts +1 -0
  184. package/dist/tests/middleware.test.js +109 -0
  185. package/dist/tests/native-adapter.test.d.ts +1 -0
  186. package/dist/tests/native-adapter.test.js +71 -0
  187. package/dist/tests/observability.test.d.ts +1 -0
  188. package/dist/tests/observability.test.js +59 -0
  189. package/dist/tests/openapi.test.d.ts +1 -0
  190. package/dist/tests/openapi.test.js +64 -0
  191. package/dist/tests/plugin.test.d.ts +1 -0
  192. package/dist/tests/plugin.test.js +65 -0
  193. package/dist/tests/plugins.test.d.ts +1 -0
  194. package/dist/tests/plugins.test.js +71 -0
  195. package/dist/tests/rate-limit.test.d.ts +1 -0
  196. package/dist/tests/rate-limit.test.js +77 -0
  197. package/dist/tests/resources.test.d.ts +1 -0
  198. package/dist/tests/resources.test.js +47 -0
  199. package/dist/tests/scheduler.test.d.ts +1 -0
  200. package/dist/tests/scheduler.test.js +46 -0
  201. package/dist/tests/schema-routes.test.d.ts +1 -0
  202. package/dist/tests/schema-routes.test.js +77 -0
  203. package/dist/tests/security.test.d.ts +1 -0
  204. package/dist/tests/security.test.js +83 -0
  205. package/dist/tests/server-db.test.d.ts +1 -0
  206. package/dist/tests/server-db.test.js +72 -0
  207. package/dist/tests/smoke.test.d.ts +1 -0
  208. package/dist/tests/smoke.test.js +10 -0
  209. package/dist/tests/sqlite-fusion.test.d.ts +1 -0
  210. package/dist/tests/sqlite-fusion.test.js +92 -0
  211. package/dist/tests/static.test.d.ts +1 -0
  212. package/dist/tests/static.test.js +102 -0
  213. package/dist/tests/stream.test.d.ts +1 -0
  214. package/dist/tests/stream.test.js +44 -0
  215. package/dist/tests/task-metrics.test.d.ts +1 -0
  216. package/dist/tests/task-metrics.test.js +53 -0
  217. package/dist/tests/tasks.test.d.ts +1 -0
  218. package/dist/tests/tasks.test.js +62 -0
  219. package/dist/tests/testing.test.d.ts +1 -0
  220. package/dist/tests/testing.test.js +47 -0
  221. package/dist/tests/validation.test.d.ts +1 -0
  222. package/dist/tests/validation.test.js +107 -0
  223. package/dist/tests/websocket.test.d.ts +1 -0
  224. package/dist/tests/websocket.test.js +146 -0
  225. package/dist/vitest.config.d.ts +2 -0
  226. package/dist/vitest.config.js +9 -0
  227. package/docs/FUSION.md +19 -0
  228. package/package.json +2 -1
  229. package/prebuilds/darwin-arm64/qhttpx.node +0 -0
  230. package/prebuilds/linux-x64/qhttpx.node +0 -0
  231. package/prebuilds/win32-x64/qhttpx.node +0 -0
  232. package/scripts/install-native.js +26 -0
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const autocannon_1 = __importDefault(require("autocannon"));
7
+ const index_1 = require("../index");
8
+ const fastify_1 = __importDefault(require("fastify"));
9
+ const express_1 = __importDefault(require("express"));
10
+ const hono_1 = require("hono");
11
+ const node_server_1 = require("@hono/node-server");
12
+ const koa_1 = __importDefault(require("koa"));
13
+ const polka_1 = __importDefault(require("polka"));
14
+ const http_1 = __importDefault(require("http"));
15
+ const PORT = 3000;
16
+ const DURATION = 300; // Testing duration
17
+ const CONNECTIONS = 500; // Connections
18
+ const PIPELINING = 20; // Request pipelining
19
+ const results = [];
20
+ function printProgress(name, timeLeft, total) {
21
+ const percent = Math.max(0, Math.min(100, Math.round(((total - timeLeft) / total) * 100)));
22
+ const barLength = 30;
23
+ const filled = Math.round((barLength * percent) / 100);
24
+ const bar = '█'.repeat(filled) + '░'.repeat(barLength - filled);
25
+ // Clear line and move cursor to start
26
+ process.stdout.clearLine(0);
27
+ process.stdout.cursorTo(0);
28
+ process.stdout.write(`${name} [${bar}] ${percent}% | Time Left: ${timeLeft}s`);
29
+ }
30
+ const frameworks = [
31
+ {
32
+ name: 'QHTTPX (Native)',
33
+ control: () => {
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ let app;
36
+ return {
37
+ start: async () => {
38
+ return new Promise((resolve) => {
39
+ app = (0, index_1.createHttpApp)();
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ app.get('/', ({ json }) => json({ hello: 'world' }));
42
+ app.listen(PORT, () => resolve());
43
+ });
44
+ },
45
+ stop: async () => {
46
+ await app.close();
47
+ },
48
+ };
49
+ },
50
+ },
51
+ {
52
+ name: 'Fastify',
53
+ control: () => {
54
+ const app = (0, fastify_1.default)();
55
+ app.get('/', async () => ({ hello: 'world' }));
56
+ return {
57
+ start: async () => {
58
+ await app.listen({ port: PORT });
59
+ },
60
+ stop: async () => {
61
+ await app.close();
62
+ },
63
+ };
64
+ },
65
+ },
66
+ {
67
+ name: 'Hono (Node)',
68
+ control: () => {
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ let server;
71
+ return {
72
+ start: async () => {
73
+ const app = new hono_1.Hono();
74
+ app.get('/', (c) => c.json({ hello: 'world' }));
75
+ return new Promise((resolve, reject) => {
76
+ try {
77
+ server = (0, node_server_1.serve)({ fetch: app.fetch, port: PORT }, () => resolve());
78
+ server.on('error', reject);
79
+ }
80
+ catch (e) {
81
+ reject(e);
82
+ }
83
+ });
84
+ },
85
+ stop: async () => {
86
+ return new Promise((resolve) => {
87
+ if (server && typeof server.close === 'function') {
88
+ server.removeAllListeners('error');
89
+ server.close(() => resolve());
90
+ }
91
+ else {
92
+ resolve();
93
+ }
94
+ });
95
+ },
96
+ };
97
+ },
98
+ },
99
+ {
100
+ name: 'Express',
101
+ control: () => {
102
+ let server;
103
+ return {
104
+ start: async () => {
105
+ return new Promise((resolve) => {
106
+ const app = (0, express_1.default)();
107
+ app.disable('etag');
108
+ app.disable('x-powered-by');
109
+ app.get('/', (req, res) => res.json({ hello: 'world' }));
110
+ server = app.listen(PORT, () => resolve());
111
+ });
112
+ },
113
+ stop: async () => {
114
+ return new Promise((resolve) => {
115
+ server.close(() => resolve());
116
+ });
117
+ },
118
+ };
119
+ },
120
+ },
121
+ {
122
+ name: 'Koa',
123
+ control: () => {
124
+ let server;
125
+ return {
126
+ start: async () => {
127
+ return new Promise((resolve) => {
128
+ const app = new koa_1.default();
129
+ app.use((ctx) => {
130
+ ctx.body = { hello: 'world' };
131
+ });
132
+ server = app.listen(PORT, () => resolve());
133
+ });
134
+ },
135
+ stop: async () => {
136
+ return new Promise((resolve) => {
137
+ server.close(() => resolve());
138
+ });
139
+ },
140
+ };
141
+ },
142
+ },
143
+ {
144
+ name: 'Polka',
145
+ control: () => {
146
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
+ let server;
148
+ return {
149
+ start: async () => {
150
+ return new Promise((resolve) => {
151
+ const app = (0, polka_1.default)();
152
+ app.get('/', (req, res) => {
153
+ res.setHeader('Content-Type', 'application/json');
154
+ res.end(JSON.stringify({ hello: 'world' }));
155
+ });
156
+ server = app.listen(PORT, () => resolve());
157
+ });
158
+ },
159
+ stop: async () => {
160
+ return new Promise((resolve) => {
161
+ server.server.close(() => resolve());
162
+ });
163
+ },
164
+ };
165
+ },
166
+ },
167
+ {
168
+ name: 'Restify',
169
+ control: () => {
170
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
+ let server;
172
+ return {
173
+ start: async () => {
174
+ return new Promise((resolve, reject) => {
175
+ try {
176
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
177
+ const restify = require('restify');
178
+ server = restify.createServer();
179
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
180
+ server.get('/', (req, res, next) => {
181
+ res.send({ hello: 'world' });
182
+ next();
183
+ });
184
+ server.listen(PORT, () => resolve());
185
+ }
186
+ catch (e) {
187
+ reject(e);
188
+ }
189
+ });
190
+ },
191
+ stop: async () => {
192
+ return new Promise((resolve) => {
193
+ if (server)
194
+ server.close(() => resolve());
195
+ else
196
+ resolve();
197
+ });
198
+ },
199
+ };
200
+ },
201
+ },
202
+ {
203
+ name: 'Node Native',
204
+ control: () => {
205
+ let server;
206
+ return {
207
+ start: async () => {
208
+ return new Promise((resolve) => {
209
+ server = http_1.default.createServer((req, res) => {
210
+ res.setHeader('Content-Type', 'application/json');
211
+ res.end(JSON.stringify({ hello: 'world' }));
212
+ });
213
+ server.listen(PORT, () => resolve());
214
+ });
215
+ },
216
+ stop: async () => {
217
+ return new Promise((resolve) => {
218
+ server.close(() => resolve());
219
+ });
220
+ },
221
+ };
222
+ },
223
+ },
224
+ ];
225
+ async function run() {
226
+ const origLog = console.log;
227
+ const origError = console.error;
228
+ const origWarn = console.warn;
229
+ origLog(`\nStarting Benchmark Comparison`);
230
+ origLog(`Duration: ${DURATION}s | Connections: ${CONNECTIONS}\n`);
231
+ for (const fw of frameworks) {
232
+ origLog(`Preparing ${fw.name}...`);
233
+ const ctrl = fw.control();
234
+ try {
235
+ await ctrl.start();
236
+ // Wait for server to be fully ready
237
+ await new Promise((r) => setTimeout(r, 500));
238
+ const startTime = Date.now();
239
+ const interval = setInterval(() => {
240
+ const elapsed = (Date.now() - startTime) / 1000;
241
+ const timeLeft = Math.max(0, Math.round(DURATION - elapsed));
242
+ printProgress(fw.name, timeLeft, DURATION);
243
+ }, 200);
244
+ // Suppress autocannon logs
245
+ console.log = () => { };
246
+ console.error = () => { };
247
+ console.warn = () => { };
248
+ const result = await (0, autocannon_1.default)({
249
+ url: `http://localhost:${PORT}`,
250
+ connections: CONNECTIONS,
251
+ pipelining: PIPELINING,
252
+ duration: DURATION,
253
+ });
254
+ console.log = origLog;
255
+ console.error = origError;
256
+ console.warn = origWarn;
257
+ clearInterval(interval);
258
+ printProgress(fw.name, 0, DURATION);
259
+ origLog('\n');
260
+ results.push({
261
+ name: fw.name,
262
+ reqs: result.requests.average,
263
+ latency: result.latency.average,
264
+ p50: result.latency.p50,
265
+ p90: result.latency.p90,
266
+ p99: result.latency.p99,
267
+ max: result.latency.max,
268
+ throughput: (result.throughput.average / 1024 / 1024).toFixed(2),
269
+ });
270
+ await ctrl.stop();
271
+ }
272
+ catch (err) {
273
+ console.log = origLog;
274
+ console.error = origError;
275
+ console.warn = origWarn;
276
+ origError(`Error benchmarking ${fw.name}:`, err);
277
+ try {
278
+ await ctrl.stop();
279
+ }
280
+ catch { }
281
+ }
282
+ // Cool down - allow time for port to be released
283
+ await new Promise((r) => setTimeout(r, 3000));
284
+ }
285
+ origLog('\n--- Final Results ---');
286
+ console.table(results.sort((a, b) => b.reqs - a.reqs));
287
+ }
288
+ run().catch(console.error);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const quantam_async_1 = require("quantam-async");
4
+ const index_1 = require("../index");
5
+ async function run() {
6
+ const app = new index_1.QHTTPX({
7
+ maxConcurrency: 512,
8
+ requestTimeoutMs: 10000,
9
+ });
10
+ app.get('/json', (ctx) => {
11
+ ctx.json({ message: 'hello from qhttpx' });
12
+ });
13
+ const { port } = await app.listen(0, '127.0.0.1');
14
+ const url = `http://127.0.0.1:${port}/json`;
15
+ const requestsPerUser = 10;
16
+ const userCount = 10000;
17
+ const maxConcurrentUsers = 1000;
18
+ const flow = (0, quantam_async_1.quantam)()
19
+ .name('user-flow')
20
+ .step(async (userId) => {
21
+ for (let i = 0; i < requestsPerUser; i += 1) {
22
+ const response = await fetch(url);
23
+ if (!response.ok) {
24
+ throw new Error(`user ${userId} request ${i} failed with status ${response.status}`);
25
+ }
26
+ await response.text();
27
+ }
28
+ return userId;
29
+ })
30
+ .retry(3, 50)
31
+ .timeout(30000);
32
+ const inputs = [];
33
+ for (let i = 0; i < userCount; i += 1) {
34
+ inputs.push(i);
35
+ }
36
+ const start = Date.now();
37
+ try {
38
+ await flow.runMany(inputs, {
39
+ concurrency: maxConcurrentUsers,
40
+ });
41
+ }
42
+ catch (err) {
43
+ console.error('Quantam runMany error', err);
44
+ }
45
+ const durationSec = (Date.now() - start) / 1000;
46
+ const totalRequests = userCount * requestsPerUser;
47
+ const rps = totalRequests / durationSec;
48
+ console.log(`Quantam bench: users=${userCount}, requestsPerUser=${requestsPerUser}, ` +
49
+ `totalRequests=${totalRequests}, duration=${durationSec.toFixed(2)}s, rps=${rps.toFixed(0)}, concurrency=${maxConcurrentUsers}`);
50
+ await app.close();
51
+ process.exit(0);
52
+ }
53
+ run().catch((err) => {
54
+ console.error(err);
55
+ process.exit(1);
56
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const autocannon_1 = __importDefault(require("autocannon"));
7
+ const index_1 = require("../index");
8
+ function runAutocannon(url) {
9
+ return new Promise((resolve, reject) => {
10
+ const instance = (0, autocannon_1.default)({
11
+ url,
12
+ connections: 100,
13
+ pipelining: 10,
14
+ duration: 40,
15
+ }, (err, result) => {
16
+ if (err) {
17
+ reject(err);
18
+ return;
19
+ }
20
+ resolve(result);
21
+ });
22
+ instance.on('error', (err) => {
23
+ reject(err);
24
+ });
25
+ });
26
+ }
27
+ async function run() {
28
+ const payloadBuffer = Buffer.from(JSON.stringify({ message: 'hello from qhttpx' }));
29
+ const app = new index_1.QHTTPX({
30
+ maxConcurrency: 512,
31
+ metricsEnabled: false,
32
+ jsonSerializer: () => payloadBuffer,
33
+ });
34
+ app.get('/json', (ctx) => {
35
+ ctx.json({ message: 'hello from qhttpx' });
36
+ });
37
+ const { port } = await app.listen(0, '127.0.0.1');
38
+ const url = `http://127.0.0.1:${port}/json`;
39
+ console.log('Running warmup (1/2)...');
40
+ await runAutocannon(url);
41
+ console.log('Running benchmark (2/2)...');
42
+ const result = await runAutocannon(url);
43
+ autocannon_1.default.printResult(result);
44
+ const totalRequests = result.requests.total;
45
+ const sent = result.requests.sent;
46
+ const avgRps = result.requests.average.toFixed(0);
47
+ const p99 = result.latency.p99.toFixed(1);
48
+ const connections = result.connections;
49
+ const pipelining = result.pipelining;
50
+ // Summary line that shows up clearly in CI/dev logs
51
+ console.log(`QHTTPX bench: total=${totalRequests} (sent=${sent}) req, ` +
52
+ `${avgRps} req/sec, p99=${p99}ms, connections=${connections}, ` +
53
+ `pipelining=${pipelining}`);
54
+ await app.close();
55
+ process.exit(0);
56
+ }
57
+ run().catch((err) => {
58
+ console.error(err);
59
+ process.exit(1);
60
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const autocannon_1 = __importDefault(require("autocannon"));
7
+ const index_1 = require("../index");
8
+ async function runAutocannon(name, url) {
9
+ const result = await (0, autocannon_1.default)({
10
+ url,
11
+ connections: 200,
12
+ pipelining: 10,
13
+ duration: 10,
14
+ });
15
+ const total = result.requests.total;
16
+ const sent = result.requests.sent;
17
+ const rps = result.requests.average;
18
+ const p99 = result.latency.p99;
19
+ console.log(`${name} bench: total=${total} (sent=${sent}) req, ` +
20
+ `${rps.toFixed(0)} req/sec, p99=${p99.toFixed(1)}ms, connections=${result.connections}, pipelining=${result.pipelining}`);
21
+ return {
22
+ name,
23
+ total,
24
+ sent,
25
+ rps,
26
+ p99,
27
+ };
28
+ }
29
+ async function startQHTTPXBalanced() {
30
+ const payloadBuffer = Buffer.from(JSON.stringify({ message: 'hello from qhttpx' }));
31
+ const app = new index_1.QHTTPX({
32
+ maxConcurrency: 1024,
33
+ metricsEnabled: false,
34
+ performanceMode: 'balanced',
35
+ jsonSerializer: () => payloadBuffer,
36
+ });
37
+ app.get('/json', (ctx) => {
38
+ ctx.json({ message: 'hello from qhttpx' });
39
+ });
40
+ const { port } = await app.listen(0, '127.0.0.1');
41
+ const url = `http://127.0.0.1:${port}/json`;
42
+ return { app, url };
43
+ }
44
+ async function startQHTTPXUltra() {
45
+ const payloadBuffer = Buffer.from(JSON.stringify({ message: 'hello from qhttpx ultra' }));
46
+ const app = new index_1.QHTTPX({
47
+ maxConcurrency: 1024,
48
+ performanceMode: 'ultra',
49
+ jsonSerializer: () => payloadBuffer,
50
+ });
51
+ app.get('/json', (ctx) => {
52
+ ctx.json({ message: 'hello from qhttpx ultra' });
53
+ });
54
+ const { port } = await app.listen(0, '127.0.0.1');
55
+ const url = `http://127.0.0.1:${port}/json`;
56
+ return { app, url };
57
+ }
58
+ async function run() {
59
+ const results = [];
60
+ console.log('=== QHTTPX Balanced Mode ===');
61
+ const balanced = await startQHTTPXBalanced();
62
+ try {
63
+ const r = await runAutocannon('QHTTPX-Balanced', balanced.url);
64
+ results.push(r);
65
+ }
66
+ finally {
67
+ await balanced.app.close();
68
+ }
69
+ console.log('\n=== QHTTPX Ultra Mode ===');
70
+ const ultra = await startQHTTPXUltra();
71
+ try {
72
+ const r = await runAutocannon('QHTTPX-Ultra', ultra.url);
73
+ results.push(r);
74
+ }
75
+ finally {
76
+ await ultra.app.close();
77
+ }
78
+ console.log('\n=== Summary ===');
79
+ for (const r of results) {
80
+ console.log(`${r.name}: ${r.rps.toFixed(0)} req/sec, p99=${r.p99.toFixed(1)}ms, total=${r.total}`);
81
+ }
82
+ // Calculate improvement
83
+ const balanced_rps = results[0]?.rps || 0;
84
+ const ultra_rps = results[1]?.rps || 0;
85
+ if (ultra_rps > 0 && balanced_rps > 0) {
86
+ const improvement = ((ultra_rps - balanced_rps) / balanced_rps * 100).toFixed(1);
87
+ console.log(`\nUltra vs Balanced improvement: ${improvement}%`);
88
+ }
89
+ process.exit(0);
90
+ }
91
+ run().catch((err) => {
92
+ console.error(err);
93
+ process.exit(1);
94
+ });
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * Buffer pool for response bodies (small, medium, large).
4
+ * Reuses allocated buffers to reduce GC pressure.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.BufferPool = void 0;
8
+ class BufferPool {
9
+ constructor(config = {}) {
10
+ this.smallBuffers = [];
11
+ this.mediumBuffers = [];
12
+ this.largeBuffers = [];
13
+ this.smallSize = config.smallSize ?? 4096; // 4KB
14
+ this.mediumSize = config.mediumSize ?? 65536; // 64KB
15
+ this.largeSize = config.largeSize ?? 262144; // 256KB
16
+ this.smallPoolSize = config.smallPoolSize ?? 32;
17
+ this.mediumPoolSize = config.mediumPoolSize ?? 8;
18
+ this.largePoolSize = config.largePoolSize ?? 2;
19
+ // Preallocate buffers
20
+ for (let i = 0; i < this.smallPoolSize; i += 1) {
21
+ this.smallBuffers.push(Buffer.allocUnsafe(this.smallSize));
22
+ }
23
+ for (let i = 0; i < this.mediumPoolSize; i += 1) {
24
+ this.mediumBuffers.push(Buffer.allocUnsafe(this.mediumSize));
25
+ }
26
+ for (let i = 0; i < this.largePoolSize; i += 1) {
27
+ this.largeBuffers.push(Buffer.allocUnsafe(this.largeSize));
28
+ }
29
+ }
30
+ /**
31
+ * Acquire a buffer suitable for the given size.
32
+ * Returns a buffer from the appropriate pool.
33
+ */
34
+ acquire(size) {
35
+ if (size <= this.smallSize) {
36
+ return this.smallBuffers.pop() || Buffer.allocUnsafe(this.smallSize);
37
+ }
38
+ if (size <= this.mediumSize) {
39
+ return this.mediumBuffers.pop() || Buffer.allocUnsafe(this.mediumSize);
40
+ }
41
+ return this.largeBuffers.pop() || Buffer.allocUnsafe(this.largeSize);
42
+ }
43
+ /**
44
+ * Release a buffer back to the appropriate pool.
45
+ */
46
+ release(buffer) {
47
+ if (buffer.length === this.smallSize && this.smallBuffers.length < this.smallPoolSize) {
48
+ this.smallBuffers.push(buffer);
49
+ }
50
+ else if (buffer.length === this.mediumSize &&
51
+ this.mediumBuffers.length < this.mediumPoolSize) {
52
+ this.mediumBuffers.push(buffer);
53
+ }
54
+ else if (buffer.length === this.largeSize &&
55
+ this.largeBuffers.length < this.largePoolSize) {
56
+ this.largeBuffers.push(buffer);
57
+ }
58
+ }
59
+ /**
60
+ * Get the number of available buffers in each pool.
61
+ */
62
+ getPoolStatus() {
63
+ return {
64
+ small: this.smallBuffers.length,
65
+ medium: this.mediumBuffers.length,
66
+ large: this.largeBuffers.length,
67
+ };
68
+ }
69
+ }
70
+ exports.BufferPool = BufferPool;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};