qhttpx 2.1.0 → 2.3.1

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 (267) hide show
  1. package/LICENSE +201 -21
  2. package/README.md +117 -221
  3. package/dist/chunk-QW72SEAS.mjs +98 -0
  4. package/dist/{src/cli/index.d.ts → cli.d.mts} +0 -1
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +287 -0
  7. package/dist/cli.mjs +209 -0
  8. package/dist/index.d.mts +433 -0
  9. package/dist/index.d.ts +433 -0
  10. package/dist/index.js +1955 -0
  11. package/dist/index.mjs +1863 -0
  12. package/dist/qhttpx-core-new.linux-x64-gnu.node +0 -0
  13. package/dist/qhttpx-core-new.node +0 -0
  14. package/dist/qhttpx-core-new.win32-x64-msvc.node +0 -0
  15. package/examples/benchmark.ts +67 -0
  16. package/examples/body_demo.ts +32 -0
  17. package/examples/chat_client.ts +36 -0
  18. package/examples/chat_demo.ts +41 -0
  19. package/examples/cluster_demo.ts +26 -0
  20. package/examples/cors_demo.ts +25 -0
  21. package/examples/db_auth_demo.ts +66 -0
  22. package/examples/demo.ts +52 -0
  23. package/examples/headers_demo.ts +24 -0
  24. package/examples/hello.ts +7 -0
  25. package/examples/http2_demo.ts +52 -0
  26. package/examples/magic-dev.ts +15 -0
  27. package/examples/middleware_demo.ts +34 -0
  28. package/examples/mongo_demo.ts +40 -0
  29. package/examples/native_policy_demo.ts +33 -0
  30. package/examples/observability_demo.ts +24 -0
  31. package/examples/public/1mb.dat +1 -0
  32. package/examples/query_demo.ts +29 -0
  33. package/examples/response_demo.ts +21 -0
  34. package/examples/routing_demo.ts +24 -0
  35. package/examples/server.ts +51 -0
  36. package/examples/static_demo.ts +29 -0
  37. package/examples/test_middleware_client.ts +33 -0
  38. package/examples/test_query_client.ts +34 -0
  39. package/examples/test_response_client.ts +30 -0
  40. package/examples/tls_demo.ts +32 -0
  41. package/examples/upload_demo.ts +43 -0
  42. package/examples/uploads/test.txt +1 -0
  43. package/examples/verify_upload.ts +69 -0
  44. package/examples/ws_client.ts +26 -0
  45. package/examples/ws_demo.ts +21 -0
  46. package/package.json +65 -81
  47. package/CHANGELOG.md +0 -285
  48. package/dist/examples/api-server.d.ts +0 -1
  49. package/dist/examples/api-server.js +0 -80
  50. package/dist/examples/basic.d.ts +0 -1
  51. package/dist/examples/basic.js +0 -9
  52. package/dist/examples/compression.d.ts +0 -1
  53. package/dist/examples/compression.js +0 -15
  54. package/dist/examples/cors.d.ts +0 -1
  55. package/dist/examples/cors.js +0 -18
  56. package/dist/examples/errors.d.ts +0 -1
  57. package/dist/examples/errors.js +0 -26
  58. package/dist/examples/file-upload.d.ts +0 -1
  59. package/dist/examples/file-upload.js +0 -22
  60. package/dist/examples/fusion.d.ts +0 -1
  61. package/dist/examples/fusion.js +0 -21
  62. package/dist/examples/rate-limiting.d.ts +0 -1
  63. package/dist/examples/rate-limiting.js +0 -17
  64. package/dist/examples/validation.d.ts +0 -1
  65. package/dist/examples/validation.js +0 -22
  66. package/dist/examples/websockets.d.ts +0 -1
  67. package/dist/examples/websockets.js +0 -19
  68. package/dist/package.json +0 -107
  69. package/dist/src/benchmarks/quantam-users.d.ts +0 -1
  70. package/dist/src/benchmarks/quantam-users.js +0 -56
  71. package/dist/src/benchmarks/quick-bench.d.ts +0 -1
  72. package/dist/src/benchmarks/quick-bench.js +0 -57
  73. package/dist/src/benchmarks/simple-json.d.ts +0 -1
  74. package/dist/src/benchmarks/simple-json.js +0 -171
  75. package/dist/src/benchmarks/ultra-mode.d.ts +0 -1
  76. package/dist/src/benchmarks/ultra-mode.js +0 -64
  77. package/dist/src/cli/index.js +0 -222
  78. package/dist/src/client/index.d.ts +0 -17
  79. package/dist/src/client/index.js +0 -72
  80. package/dist/src/core/batch.d.ts +0 -24
  81. package/dist/src/core/batch.js +0 -97
  82. package/dist/src/core/body-parser.d.ts +0 -15
  83. package/dist/src/core/body-parser.js +0 -121
  84. package/dist/src/core/buffer-pool.d.ts +0 -41
  85. package/dist/src/core/buffer-pool.js +0 -70
  86. package/dist/src/core/config.d.ts +0 -7
  87. package/dist/src/core/config.js +0 -50
  88. package/dist/src/core/context-pool.d.ts +0 -12
  89. package/dist/src/core/context-pool.js +0 -34
  90. package/dist/src/core/errors.d.ts +0 -34
  91. package/dist/src/core/errors.js +0 -70
  92. package/dist/src/core/fusion.d.ts +0 -20
  93. package/dist/src/core/fusion.js +0 -191
  94. package/dist/src/core/logger.d.ts +0 -22
  95. package/dist/src/core/logger.js +0 -49
  96. package/dist/src/core/metrics.d.ts +0 -50
  97. package/dist/src/core/metrics.js +0 -123
  98. package/dist/src/core/resources.d.ts +0 -9
  99. package/dist/src/core/resources.js +0 -25
  100. package/dist/src/core/scheduler.d.ts +0 -38
  101. package/dist/src/core/scheduler.js +0 -126
  102. package/dist/src/core/scope.d.ts +0 -41
  103. package/dist/src/core/scope.js +0 -107
  104. package/dist/src/core/serializer.d.ts +0 -10
  105. package/dist/src/core/serializer.js +0 -82
  106. package/dist/src/core/server.d.ts +0 -179
  107. package/dist/src/core/server.js +0 -1511
  108. package/dist/src/core/stream.d.ts +0 -15
  109. package/dist/src/core/stream.js +0 -71
  110. package/dist/src/core/tasks.d.ts +0 -29
  111. package/dist/src/core/tasks.js +0 -87
  112. package/dist/src/core/timer.d.ts +0 -11
  113. package/dist/src/core/timer.js +0 -29
  114. package/dist/src/core/types.d.ts +0 -225
  115. package/dist/src/core/types.js +0 -19
  116. package/dist/src/core/websocket.d.ts +0 -25
  117. package/dist/src/core/websocket.js +0 -86
  118. package/dist/src/core/worker-queue.d.ts +0 -41
  119. package/dist/src/core/worker-queue.js +0 -73
  120. package/dist/src/database/adapters/memory.d.ts +0 -21
  121. package/dist/src/database/adapters/memory.js +0 -90
  122. package/dist/src/database/adapters/mongo.d.ts +0 -11
  123. package/dist/src/database/adapters/mongo.js +0 -141
  124. package/dist/src/database/adapters/postgres.d.ts +0 -10
  125. package/dist/src/database/adapters/postgres.js +0 -111
  126. package/dist/src/database/adapters/sqlite.d.ts +0 -10
  127. package/dist/src/database/adapters/sqlite.js +0 -42
  128. package/dist/src/database/coalescer.d.ts +0 -14
  129. package/dist/src/database/coalescer.js +0 -134
  130. package/dist/src/database/manager.d.ts +0 -35
  131. package/dist/src/database/manager.js +0 -87
  132. package/dist/src/database/types.d.ts +0 -20
  133. package/dist/src/database/types.js +0 -2
  134. package/dist/src/index.d.ts +0 -52
  135. package/dist/src/index.js +0 -92
  136. package/dist/src/middleware/compression.d.ts +0 -2
  137. package/dist/src/middleware/compression.js +0 -133
  138. package/dist/src/middleware/cors.d.ts +0 -2
  139. package/dist/src/middleware/cors.js +0 -66
  140. package/dist/src/middleware/presets.d.ts +0 -15
  141. package/dist/src/middleware/presets.js +0 -52
  142. package/dist/src/middleware/rate-limit.d.ts +0 -14
  143. package/dist/src/middleware/rate-limit.js +0 -83
  144. package/dist/src/middleware/security.d.ts +0 -10
  145. package/dist/src/middleware/security.js +0 -74
  146. package/dist/src/middleware/static.d.ts +0 -11
  147. package/dist/src/middleware/static.js +0 -191
  148. package/dist/src/openapi/generator.d.ts +0 -19
  149. package/dist/src/openapi/generator.js +0 -149
  150. package/dist/src/router/radix-router.d.ts +0 -18
  151. package/dist/src/router/radix-router.js +0 -89
  152. package/dist/src/router/radix-tree.d.ts +0 -21
  153. package/dist/src/router/radix-tree.js +0 -175
  154. package/dist/src/router/router.d.ts +0 -37
  155. package/dist/src/router/router.js +0 -203
  156. package/dist/src/testing/index.d.ts +0 -25
  157. package/dist/src/testing/index.js +0 -84
  158. package/dist/src/utils/cookies.d.ts +0 -3
  159. package/dist/src/utils/cookies.js +0 -59
  160. package/dist/src/utils/logger.d.ts +0 -2
  161. package/dist/src/utils/logger.js +0 -45
  162. package/dist/src/utils/signals.d.ts +0 -6
  163. package/dist/src/utils/signals.js +0 -31
  164. package/dist/src/utils/sse.d.ts +0 -6
  165. package/dist/src/utils/sse.js +0 -32
  166. package/dist/src/validation/index.d.ts +0 -3
  167. package/dist/src/validation/index.js +0 -19
  168. package/dist/src/validation/simple.d.ts +0 -5
  169. package/dist/src/validation/simple.js +0 -102
  170. package/dist/src/validation/types.d.ts +0 -32
  171. package/dist/src/validation/types.js +0 -12
  172. package/dist/src/validation/zod.d.ts +0 -4
  173. package/dist/src/validation/zod.js +0 -18
  174. package/dist/src/views/index.d.ts +0 -1
  175. package/dist/src/views/index.js +0 -17
  176. package/dist/src/views/types.d.ts +0 -3
  177. package/dist/src/views/types.js +0 -2
  178. package/dist/tests/adapters.test.d.ts +0 -1
  179. package/dist/tests/adapters.test.js +0 -106
  180. package/dist/tests/batch.test.d.ts +0 -1
  181. package/dist/tests/batch.test.js +0 -117
  182. package/dist/tests/body-parser.test.d.ts +0 -1
  183. package/dist/tests/body-parser.test.js +0 -52
  184. package/dist/tests/compression-sse.test.d.ts +0 -1
  185. package/dist/tests/compression-sse.test.js +0 -87
  186. package/dist/tests/cookies.test.d.ts +0 -1
  187. package/dist/tests/cookies.test.js +0 -63
  188. package/dist/tests/cors.test.d.ts +0 -1
  189. package/dist/tests/cors.test.js +0 -55
  190. package/dist/tests/database.test.d.ts +0 -1
  191. package/dist/tests/database.test.js +0 -80
  192. package/dist/tests/dx.test.d.ts +0 -1
  193. package/dist/tests/dx.test.js +0 -114
  194. package/dist/tests/ecosystem.test.d.ts +0 -1
  195. package/dist/tests/ecosystem.test.js +0 -133
  196. package/dist/tests/features.test.d.ts +0 -1
  197. package/dist/tests/features.test.js +0 -47
  198. package/dist/tests/fusion.test.d.ts +0 -1
  199. package/dist/tests/fusion.test.js +0 -92
  200. package/dist/tests/http-basic.test.d.ts +0 -1
  201. package/dist/tests/http-basic.test.js +0 -124
  202. package/dist/tests/logger.test.d.ts +0 -1
  203. package/dist/tests/logger.test.js +0 -33
  204. package/dist/tests/middleware.test.d.ts +0 -1
  205. package/dist/tests/middleware.test.js +0 -109
  206. package/dist/tests/observability.test.d.ts +0 -1
  207. package/dist/tests/observability.test.js +0 -59
  208. package/dist/tests/openapi.test.d.ts +0 -1
  209. package/dist/tests/openapi.test.js +0 -64
  210. package/dist/tests/plugin.test.d.ts +0 -1
  211. package/dist/tests/plugin.test.js +0 -65
  212. package/dist/tests/plugins.test.d.ts +0 -1
  213. package/dist/tests/plugins.test.js +0 -71
  214. package/dist/tests/rate-limit.test.d.ts +0 -1
  215. package/dist/tests/rate-limit.test.js +0 -77
  216. package/dist/tests/resources.test.d.ts +0 -1
  217. package/dist/tests/resources.test.js +0 -47
  218. package/dist/tests/scheduler.test.d.ts +0 -1
  219. package/dist/tests/scheduler.test.js +0 -46
  220. package/dist/tests/schema-routes.test.d.ts +0 -1
  221. package/dist/tests/schema-routes.test.js +0 -79
  222. package/dist/tests/security.test.d.ts +0 -1
  223. package/dist/tests/security.test.js +0 -83
  224. package/dist/tests/server-db.test.d.ts +0 -1
  225. package/dist/tests/server-db.test.js +0 -72
  226. package/dist/tests/smoke.test.d.ts +0 -1
  227. package/dist/tests/smoke.test.js +0 -10
  228. package/dist/tests/sqlite-fusion.test.d.ts +0 -1
  229. package/dist/tests/sqlite-fusion.test.js +0 -92
  230. package/dist/tests/static.test.d.ts +0 -1
  231. package/dist/tests/static.test.js +0 -102
  232. package/dist/tests/stream.test.d.ts +0 -1
  233. package/dist/tests/stream.test.js +0 -44
  234. package/dist/tests/task-metrics.test.d.ts +0 -1
  235. package/dist/tests/task-metrics.test.js +0 -53
  236. package/dist/tests/tasks.test.d.ts +0 -1
  237. package/dist/tests/tasks.test.js +0 -62
  238. package/dist/tests/testing.test.d.ts +0 -1
  239. package/dist/tests/testing.test.js +0 -47
  240. package/dist/tests/validation.test.d.ts +0 -1
  241. package/dist/tests/validation.test.js +0 -107
  242. package/dist/tests/websocket.test.d.ts +0 -1
  243. package/dist/tests/websocket.test.js +0 -146
  244. package/dist/vitest.config.d.ts +0 -2
  245. package/dist/vitest.config.js +0 -9
  246. package/docs/AEGIS.md +0 -91
  247. package/docs/API_REFERENCE.md +0 -749
  248. package/docs/BENCHMARKS.md +0 -39
  249. package/docs/CAPABILITIES.md +0 -70
  250. package/docs/CLI.md +0 -43
  251. package/docs/DATABASE.md +0 -142
  252. package/docs/ECOSYSTEM.md +0 -146
  253. package/docs/ERRORS.md +0 -112
  254. package/docs/FUSION.md +0 -87
  255. package/docs/MIDDLEWARE.md +0 -65
  256. package/docs/MIGRATION_1.9_TO_2.0.md +0 -495
  257. package/docs/NEXT_STEPS.md +0 -99
  258. package/docs/OPENAPI.md +0 -99
  259. package/docs/PLUGINS.md +0 -59
  260. package/docs/PRODUCTION_DEPLOYMENT.md +0 -798
  261. package/docs/REAL_WORLD_EXAMPLES.md +0 -109
  262. package/docs/ROADMAP.md +0 -366
  263. package/docs/ROUTING.md +0 -78
  264. package/docs/SECURITY.md +0 -876
  265. package/docs/STATIC.md +0 -61
  266. package/docs/VALIDATION.md +0 -114
  267. package/docs/WEBSOCKETS.md +0 -76
@@ -1,124 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const index_1 = require("../src/index");
5
- (0, vitest_1.describe)('QHTTPX minimal HTTP runtime', () => {
6
- let app;
7
- (0, vitest_1.afterEach)(async () => {
8
- if (app) {
9
- await app.close();
10
- app = undefined;
11
- }
12
- });
13
- (0, vitest_1.it)('handles GET / and returns JSON', async () => {
14
- app = new index_1.QHTTPX();
15
- app.get('/', (ctx) => {
16
- ctx.json({ message: 'Hello from QHTTPX' });
17
- });
18
- const { port } = await app.listen(0, '127.0.0.1');
19
- const response = await fetch(`http://127.0.0.1:${port}/`);
20
- (0, vitest_1.expect)(response.status).toBe(200);
21
- const json = (await response.json());
22
- (0, vitest_1.expect)(json.message).toBe('Hello from QHTTPX');
23
- });
24
- (0, vitest_1.it)('returns 404 for unknown route', async () => {
25
- app = new index_1.QHTTPX();
26
- const { port } = await app.listen(0, '127.0.0.1');
27
- const response = await fetch(`http://127.0.0.1:${port}/unknown`);
28
- (0, vitest_1.expect)(response.status).toBe(404);
29
- });
30
- (0, vitest_1.it)('supports params and query parsing', async () => {
31
- app = new index_1.QHTTPX();
32
- app.get('/users/:id', (ctx) => {
33
- ctx.json({
34
- id: ctx.params.id,
35
- q: ctx.query.q,
36
- });
37
- });
38
- const { port } = await app.listen(0, '127.0.0.1');
39
- const response = await fetch(`http://127.0.0.1:${port}/users/123?q=test`);
40
- (0, vitest_1.expect)(response.status).toBe(200);
41
- const json = (await response.json());
42
- (0, vitest_1.expect)(json.id).toBe('123');
43
- (0, vitest_1.expect)(json.q).toBe('test');
44
- });
45
- (0, vitest_1.it)('parses JSON body for POST', async () => {
46
- app = new index_1.QHTTPX();
47
- app.post('/echo', (ctx) => {
48
- ctx.json({ body: ctx.body });
49
- });
50
- const { port } = await app.listen(0, '127.0.0.1');
51
- const response = await fetch(`http://127.0.0.1:${port}/echo`, {
52
- method: 'POST',
53
- headers: {
54
- 'content-type': 'application/json',
55
- },
56
- body: JSON.stringify({ hello: 'world' }),
57
- });
58
- (0, vitest_1.expect)(response.status).toBe(200);
59
- const json = (await response.json());
60
- (0, vitest_1.expect)(json.body.hello).toBe('world');
61
- });
62
- (0, vitest_1.it)('returns 400 for invalid JSON body', async () => {
63
- app = new index_1.QHTTPX();
64
- const { port } = await app.listen(0, '127.0.0.1');
65
- app.post('/echo', (ctx) => {
66
- ctx.json({ body: ctx.body });
67
- });
68
- const response = await fetch(`http://127.0.0.1:${port}/echo`, {
69
- method: 'POST',
70
- headers: {
71
- 'content-type': 'application/json',
72
- },
73
- body: '{ invalid',
74
- });
75
- (0, vitest_1.expect)(response.status).toBe(400);
76
- const text = await response.text();
77
- (0, vitest_1.expect)(text).toBe('Invalid JSON');
78
- });
79
- (0, vitest_1.it)('enforces maxBodyBytes with 413 status', async () => {
80
- app = new index_1.QHTTPX({ maxBodyBytes: 8 });
81
- app.post('/echo', (ctx) => {
82
- ctx.json({ body: ctx.body });
83
- });
84
- const { port } = await app.listen(0, '127.0.0.1');
85
- const response = await fetch(`http://127.0.0.1:${port}/echo`, {
86
- method: 'POST',
87
- headers: {
88
- 'content-type': 'application/json',
89
- },
90
- body: JSON.stringify({ hello: 'world' }),
91
- });
92
- (0, vitest_1.expect)(response.status).toBe(413);
93
- const text = await response.text();
94
- (0, vitest_1.expect)(text).toBe('Payload Too Large');
95
- });
96
- (0, vitest_1.it)('enforces maxConcurrency with 503 overload', async () => {
97
- app = new index_1.QHTTPX({ maxConcurrency: 1 });
98
- app.get('/slow', async (ctx) => {
99
- await new Promise((resolve) => {
100
- setTimeout(resolve, 100);
101
- });
102
- ctx.json({ ok: true });
103
- });
104
- const { port } = await app.listen(0, '127.0.0.1');
105
- const url = `http://127.0.0.1:${port}/slow`;
106
- const first = fetch(url);
107
- const second = fetch(url);
108
- const [firstRes, secondRes] = await Promise.all([first, second]);
109
- const statuses = [firstRes.status, secondRes.status].sort();
110
- (0, vitest_1.expect)(statuses).toEqual([200, 503]);
111
- });
112
- (0, vitest_1.it)('applies request timeout with 504 status', async () => {
113
- app = new index_1.QHTTPX({ requestTimeoutMs: 20 });
114
- app.get('/timeout', async (ctx) => {
115
- await new Promise((resolve) => {
116
- setTimeout(resolve, 100);
117
- });
118
- ctx.json({ ok: true });
119
- });
120
- const { port } = await app.listen(0, '127.0.0.1');
121
- const response = await fetch(`http://127.0.0.1:${port}/timeout`);
122
- (0, vitest_1.expect)(response.status).toBe(504);
123
- });
124
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,33 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const src_1 = require("../src");
5
- (0, vitest_1.describe)('Logger middleware', () => {
6
- let app;
7
- (0, vitest_1.afterEach)(async () => {
8
- if (app) {
9
- await app.close();
10
- app = undefined;
11
- }
12
- });
13
- (0, vitest_1.it)('logs basic request information to sink', async () => {
14
- const sink = vitest_1.vi.fn();
15
- app = new src_1.QHTTPX();
16
- app.use((0, src_1.createLoggerMiddleware)({
17
- sink,
18
- }));
19
- app.get('/hello', (ctx) => {
20
- ctx.send('ok');
21
- });
22
- const { port } = await app.listen(0, '127.0.0.1');
23
- const response = await fetch(`http://127.0.0.1:${port}/hello`);
24
- (0, vitest_1.expect)(response.status).toBe(200);
25
- (0, vitest_1.expect)(await response.text()).toBe('ok');
26
- (0, vitest_1.expect)(sink).toHaveBeenCalledTimes(1);
27
- const entry = sink.mock.calls[0][0];
28
- (0, vitest_1.expect)(entry.method).toBe('GET');
29
- (0, vitest_1.expect)(entry.path).toBe('/hello');
30
- (0, vitest_1.expect)(entry.status).toBe(200);
31
- (0, vitest_1.expect)(entry.durationMs).toBeGreaterThanOrEqual(0);
32
- });
33
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,109 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const index_1 = require("../src/index");
5
- (0, vitest_1.describe)('QHTTPX middleware pipeline', () => {
6
- let app;
7
- (0, vitest_1.afterEach)(async () => {
8
- if (app) {
9
- await app.close();
10
- app = undefined;
11
- }
12
- });
13
- (0, vitest_1.it)('runs middleware in order with await next()', async () => {
14
- app = new index_1.QHTTPX();
15
- const calls = [];
16
- app.use(async (ctx, next) => {
17
- calls.push('m1-before');
18
- await next();
19
- calls.push('m1-after');
20
- });
21
- app.use(async (ctx, next) => {
22
- calls.push('m2-before');
23
- await next();
24
- calls.push('m2-after');
25
- });
26
- app.get('/', (ctx) => {
27
- calls.push('handler');
28
- ctx.send('ok');
29
- });
30
- const { port } = await app.listen(0, '127.0.0.1');
31
- const response = await fetch(`http://127.0.0.1:${port}/`);
32
- (0, vitest_1.expect)(response.status).toBe(200);
33
- (0, vitest_1.expect)(calls).toEqual([
34
- 'm1-before',
35
- 'm2-before',
36
- 'handler',
37
- 'm2-after',
38
- 'm1-after',
39
- ]);
40
- });
41
- (0, vitest_1.it)('allows middleware to modify response headers', async () => {
42
- app = new index_1.QHTTPX();
43
- app.use((ctx, next) => {
44
- ctx.res.setHeader('x-powered-by', 'qhttpx');
45
- return next();
46
- });
47
- app.get('/', (ctx) => {
48
- ctx.send('ok');
49
- });
50
- const { port } = await app.listen(0, '127.0.0.1');
51
- const response = await fetch(`http://127.0.0.1:${port}/`);
52
- (0, vitest_1.expect)(response.status).toBe(200);
53
- const header = response.headers.get('x-powered-by');
54
- (0, vitest_1.expect)(header).toBe('qhttpx');
55
- });
56
- (0, vitest_1.it)('supports auth middleware that short-circuits unauthorized requests', async () => {
57
- app = new index_1.QHTTPX();
58
- app.use((ctx, next) => {
59
- const auth = ctx.req.headers['authorization'];
60
- if (auth !== 'Bearer secret') {
61
- ctx.res.statusCode = 401;
62
- ctx.res.end('Unauthorized');
63
- return;
64
- }
65
- return next();
66
- });
67
- app.get('/secure', (ctx) => {
68
- ctx.send('ok');
69
- });
70
- const { port } = await app.listen(0, '127.0.0.1');
71
- const unauthorized = await fetch(`http://127.0.0.1:${port}/secure`);
72
- (0, vitest_1.expect)(unauthorized.status).toBe(401);
73
- const unauthorizedText = await unauthorized.text();
74
- (0, vitest_1.expect)(unauthorizedText).toBe('Unauthorized');
75
- const authorized = await fetch(`http://127.0.0.1:${port}/secure`, {
76
- headers: {
77
- authorization: 'Bearer secret',
78
- },
79
- });
80
- (0, vitest_1.expect)(authorized.status).toBe(200);
81
- const body = await authorized.text();
82
- (0, vitest_1.expect)(body).toBe('ok');
83
- });
84
- (0, vitest_1.it)('supports error-handling middleware that converts errors to JSON', async () => {
85
- app = new index_1.QHTTPX();
86
- app.use(async (ctx, next) => {
87
- try {
88
- await next();
89
- }
90
- catch (err) {
91
- ctx.res.statusCode = 500;
92
- ctx.res.setHeader('content-type', 'application/json; charset=utf-8');
93
- ctx.res.end(JSON.stringify({
94
- error: 'internal',
95
- message: err instanceof Error ? err.message : 'unknown',
96
- }));
97
- }
98
- });
99
- app.get('/boom', () => {
100
- throw new Error('boom');
101
- });
102
- const { port } = await app.listen(0, '127.0.0.1');
103
- const response = await fetch(`http://127.0.0.1:${port}/boom`);
104
- (0, vitest_1.expect)(response.status).toBe(500);
105
- const json = (await response.json());
106
- (0, vitest_1.expect)(json.error).toBe('internal');
107
- (0, vitest_1.expect)(json.message).toBe('boom');
108
- });
109
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,59 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const index_1 = require("../src/index");
5
- (0, vitest_1.describe)('QHTTPX observability and health', () => {
6
- let app;
7
- (0, vitest_1.afterEach)(async () => {
8
- if (app) {
9
- await app.close();
10
- app = undefined;
11
- }
12
- });
13
- (0, vitest_1.it)('exposes a health endpoint with basic info', async () => {
14
- app = new index_1.QHTTPX();
15
- const { port } = await app.listen(0, '127.0.0.1');
16
- const response = await fetch(`http://127.0.0.1:${port}/__qhttpx/health`);
17
- (0, vitest_1.expect)(response.status).toBe(200);
18
- const json = (await response.json());
19
- (0, vitest_1.expect)(json.status).toBe('ok');
20
- (0, vitest_1.expect)(typeof json.name).toBe('string');
21
- (0, vitest_1.expect)(typeof json.version).toBe('string');
22
- (0, vitest_1.expect)(json.workers).toBeGreaterThan(0);
23
- });
24
- (0, vitest_1.it)('exposes metrics for handled requests', async () => {
25
- app = new index_1.QHTTPX();
26
- app.get('/ping', (ctx) => {
27
- ctx.send('pong');
28
- });
29
- const { port } = await app.listen(0, '127.0.0.1');
30
- await fetch(`http://127.0.0.1:${port}/ping`);
31
- await fetch(`http://127.0.0.1:${port}/ping`);
32
- const response = await fetch(`http://127.0.0.1:${port}/__qhttpx/metrics`);
33
- (0, vitest_1.expect)(response.status).toBe(200);
34
- const json = (await response.json());
35
- (0, vitest_1.expect)(json.totalRequests).toBeGreaterThanOrEqual(2);
36
- (0, vitest_1.expect)(json.inFlightRequests).toBeGreaterThanOrEqual(0);
37
- (0, vitest_1.expect)(json.latency.p50 === null || json.latency.p50 >= 0).toBe(true);
38
- (0, vitest_1.expect)(json.memory.rssBytes).toBeGreaterThan(0);
39
- (0, vitest_1.expect)(json.memory.heapUsedBytes).toBeGreaterThan(0);
40
- (0, vitest_1.expect)(json.workers).toBeGreaterThan(0);
41
- });
42
- (0, vitest_1.it)('propagates and generates x-request-id headers', async () => {
43
- app = new index_1.QHTTPX();
44
- app.get('/ping', (ctx) => {
45
- ctx.send('pong');
46
- });
47
- const { port } = await app.listen(0, '127.0.0.1');
48
- const first = await fetch(`http://127.0.0.1:${port}/ping`);
49
- const firstId = first.headers.get('x-request-id');
50
- (0, vitest_1.expect)(firstId === null || firstId.length > 0).toBe(true);
51
- const second = await fetch(`http://127.0.0.1:${port}/ping`, {
52
- headers: {
53
- 'x-request-id': 'custom-id-123',
54
- },
55
- });
56
- const secondId = second.headers.get('x-request-id');
57
- (0, vitest_1.expect)(secondId).toBe('custom-id-123');
58
- });
59
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,64 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const server_1 = require("../src/core/server");
5
- (0, vitest_1.describe)('OpenAPI Generator', () => {
6
- (0, vitest_1.it)('should generate valid OpenAPI spec from routes', () => {
7
- const app = new server_1.QHTTPX();
8
- const userSchema = {
9
- params: {
10
- type: 'object',
11
- properties: {
12
- id: { type: 'string' }
13
- }
14
- },
15
- body: {
16
- type: 'object',
17
- properties: {
18
- name: { type: 'string' },
19
- age: { type: 'number', min: 18 }
20
- }
21
- },
22
- response: {
23
- type: 'object',
24
- properties: {
25
- success: { type: 'boolean' }
26
- }
27
- }
28
- };
29
- app.post('/users/:id', {
30
- schema: userSchema,
31
- handler: (ctx) => { ctx.json({ success: true }); }
32
- });
33
- app.get('/health', (ctx) => { ctx.send('ok'); });
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- const spec = app.getOpenAPI({
36
- info: {
37
- title: 'Test API',
38
- version: '1.0.0'
39
- }
40
- });
41
- // Check basic structure
42
- (0, vitest_1.expect)(spec.openapi).toBe('3.0.0');
43
- (0, vitest_1.expect)(spec.info.title).toBe('Test API');
44
- // Check Path normalization
45
- (0, vitest_1.expect)(spec.paths['/users/{id}']).toBeDefined();
46
- (0, vitest_1.expect)(spec.paths['/health']).toBeDefined();
47
- // Check Operation
48
- const postUser = spec.paths['/users/{id}'].post;
49
- (0, vitest_1.expect)(postUser).toBeDefined();
50
- // Check Params
51
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
- const pathParam = postUser.parameters.find((p) => p.name === 'id');
53
- (0, vitest_1.expect)(pathParam).toBeDefined();
54
- (0, vitest_1.expect)(pathParam.in).toBe('path');
55
- (0, vitest_1.expect)(pathParam.required).toBe(true);
56
- // Check Request Body
57
- const bodySchema = postUser.requestBody.content['application/json'].schema;
58
- (0, vitest_1.expect)(bodySchema.properties.name.type).toBe('string');
59
- (0, vitest_1.expect)(bodySchema.properties.age.minimum).toBe(18);
60
- // Check Response
61
- const responseSchema = postUser.responses['200'].content['application/json'].schema;
62
- (0, vitest_1.expect)(responseSchema.properties.success.type).toBe('boolean');
63
- });
64
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,65 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const server_1 = require("../src/core/server");
5
- (0, vitest_1.describe)('Plugin System', () => {
6
- (0, vitest_1.it)('should register a simple plugin', async () => {
7
- const app = new server_1.QHTTPX();
8
- let pluginRun = false;
9
- const myPlugin = async (scope) => {
10
- pluginRun = true;
11
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- scope.get('/ping', (ctx) => ctx.json({ pong: true }));
13
- };
14
- await app.register(myPlugin);
15
- (0, vitest_1.expect)(pluginRun).toBe(true);
16
- const { port } = await app.listen(0);
17
- const res = await fetch(`http://localhost:${port}/ping`);
18
- (0, vitest_1.expect)(res.status).toBe(200);
19
- (0, vitest_1.expect)(await res.json()).toEqual({ pong: true });
20
- });
21
- (0, vitest_1.it)('should handle prefixes', async () => {
22
- const app = new server_1.QHTTPX();
23
- const apiPlugin = async (scope) => {
24
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
- scope.get('/users', (ctx) => ctx.json({ users: [] }));
26
- };
27
- // Register with prefix /v1
28
- await app.register(apiPlugin, { prefix: '/v1' });
29
- const { port } = await app.listen(0);
30
- // /v1/users should exist
31
- const res = await fetch(`http://localhost:${port}/v1/users`);
32
- (0, vitest_1.expect)(res.status).toBe(200);
33
- // /users should NOT exist
34
- const res404 = await fetch(`http://localhost:${port}/users`);
35
- (0, vitest_1.expect)(res404.status).toBe(404);
36
- });
37
- (0, vitest_1.it)('should handle nested plugins with concatenated prefixes', async () => {
38
- const app = new server_1.QHTTPX();
39
- const usersPlugin = async (scope) => {
40
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
- scope.get('/list', (ctx) => ctx.json({ list: true }));
42
- };
43
- const v1Plugin = async (scope) => {
44
- // Register nested plugin under /users
45
- await scope.register(usersPlugin, { prefix: '/users' });
46
- };
47
- // Register v1 under /api
48
- await app.register(v1Plugin, { prefix: '/api' });
49
- const { port } = await app.listen(0);
50
- // Result should be /api/users/list
51
- const res = await fetch(`http://localhost:${port}/api/users/list`);
52
- (0, vitest_1.expect)(res.status).toBe(200);
53
- (0, vitest_1.expect)(await res.json()).toEqual({ list: true });
54
- });
55
- (0, vitest_1.it)('should pass options to plugins', async () => {
56
- const app = new server_1.QHTTPX();
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
- let receivedOpts;
59
- const configPlugin = async (scope, opts) => {
60
- receivedOpts = opts;
61
- };
62
- await app.register(configPlugin, { prefix: '/a', secret: '123' });
63
- (0, vitest_1.expect)(receivedOpts).toEqual({ prefix: '/a', secret: '123' });
64
- });
65
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,71 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const server_1 = require("../src/core/server");
5
- const testing_1 = require("../src/testing");
6
- (0, vitest_1.describe)('Plugin System', () => {
7
- (0, vitest_1.it)('should register a simple plugin', async () => {
8
- const app = new server_1.QHTTPX();
9
- const myPlugin = (s) => {
10
- const scope = s;
11
- scope.get('/plugin', (ctx) => {
12
- ctx.json({ message: 'Hello from Plugin' });
13
- });
14
- };
15
- await app.register(myPlugin);
16
- const client = (0, testing_1.createTestClient)(app);
17
- const res = await client.get('/plugin');
18
- (0, vitest_1.expect)(res.status).toBe(200);
19
- (0, vitest_1.expect)(await res.json()).toEqual({ message: 'Hello from Plugin' });
20
- await client.stop();
21
- });
22
- (0, vitest_1.it)('should support scoped prefixes', async () => {
23
- const app = new server_1.QHTTPX();
24
- const v1Plugin = (s) => {
25
- const scope = s;
26
- scope.get('/users', (ctx) => {
27
- ctx.json({ version: 'v1' });
28
- });
29
- };
30
- await app.register(v1Plugin, { prefix: '/v1' });
31
- const client = (0, testing_1.createTestClient)(app);
32
- const res = await client.get('/v1/users');
33
- (0, vitest_1.expect)(res.status).toBe(200);
34
- (0, vitest_1.expect)(await res.json()).toEqual({ version: 'v1' });
35
- await client.stop();
36
- });
37
- (0, vitest_1.it)('should support nested plugins', async () => {
38
- const app = new server_1.QHTTPX();
39
- const usersPlugin = (s) => {
40
- const scope = s;
41
- scope.get('/list', (ctx) => {
42
- ctx.json({ users: [] });
43
- });
44
- };
45
- const apiPlugin = async (s) => {
46
- const scope = s;
47
- await scope.register(usersPlugin, { prefix: '/users' });
48
- };
49
- await app.register(apiPlugin, { prefix: '/api' });
50
- const client = (0, testing_1.createTestClient)(app);
51
- const res = await client.get('/api/users/list');
52
- (0, vitest_1.expect)(res.status).toBe(200);
53
- (0, vitest_1.expect)(await res.json()).toEqual({ users: [] });
54
- await client.stop();
55
- });
56
- (0, vitest_1.it)('should support plugin options', async () => {
57
- const app = new server_1.QHTTPX();
58
- const authPlugin = (s, options) => {
59
- const scope = s;
60
- scope.get('/secret', (ctx) => {
61
- ctx.json({ secret: options.secret });
62
- });
63
- };
64
- await app.register(authPlugin, { secret: 'super-secret' });
65
- const client = (0, testing_1.createTestClient)(app);
66
- const res = await client.get('/secret');
67
- (0, vitest_1.expect)(res.status).toBe(200);
68
- (0, vitest_1.expect)(await res.json()).toEqual({ secret: 'super-secret' });
69
- await client.stop();
70
- });
71
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const server_1 = require("../src/core/server");
5
- const rate_limit_1 = require("../src/middleware/rate-limit");
6
- (0, vitest_1.describe)('Aegis Rate Limiter', () => {
7
- let app;
8
- let port;
9
- (0, vitest_1.beforeEach)(async () => {
10
- app = new server_1.QHTTPX();
11
- });
12
- (0, vitest_1.afterEach)(() => {
13
- // Cleanup if needed
14
- });
15
- (0, vitest_1.it)('should block requests exceeding the limit', async () => {
16
- app.use((0, rate_limit_1.rateLimit)({
17
- windowMs: 1000,
18
- max: 2, // Allow 2 requests
19
- message: { error: 'Blocked' }
20
- }));
21
- app.get('/', (ctx) => ctx.json({ ok: true }));
22
- const { port: p } = await app.listen(0);
23
- port = p;
24
- const url = `http://localhost:${port}`;
25
- // 1st request (OK)
26
- const r1 = await fetch(url);
27
- (0, vitest_1.expect)(r1.status).toBe(200);
28
- (0, vitest_1.expect)(r1.headers.get('x-ratelimit-remaining')).toBe('1');
29
- // 2nd request (OK)
30
- const r2 = await fetch(url);
31
- (0, vitest_1.expect)(r2.status).toBe(200);
32
- (0, vitest_1.expect)(r2.headers.get('x-ratelimit-remaining')).toBe('0');
33
- // 3rd request (Blocked)
34
- const r3 = await fetch(url);
35
- (0, vitest_1.expect)(r3.status).toBe(429);
36
- (0, vitest_1.expect)(await r3.json()).toEqual({ error: 'Blocked' });
37
- (0, vitest_1.expect)(r3.headers.get('retry-after')).toBeDefined();
38
- });
39
- (0, vitest_1.it)('should reset after window expires', async () => {
40
- app.use((0, rate_limit_1.rateLimit)({
41
- windowMs: 1000, // Short window
42
- max: 1
43
- }));
44
- app.get('/', (ctx) => ctx.json({ ok: true }));
45
- const { port: p } = await app.listen(0);
46
- const url = `http://localhost:${p}`;
47
- // 1st (OK)
48
- await fetch(url);
49
- // 2nd (Blocked)
50
- const r2 = await fetch(url);
51
- (0, vitest_1.expect)(r2.status).toBe(429);
52
- // Wait for window
53
- await new Promise(r => setTimeout(r, 1100));
54
- // 3rd (OK)
55
- const r3 = await fetch(url);
56
- (0, vitest_1.expect)(r3.status).toBe(200);
57
- });
58
- (0, vitest_1.it)('should support custom key generators (e.g. per API key)', async () => {
59
- app.use((0, rate_limit_1.rateLimit)({
60
- windowMs: 1000,
61
- max: 1,
62
- keyGenerator: (ctx) => ctx.req.headers['x-api-key'] || 'anon'
63
- }));
64
- app.get('/', (ctx) => ctx.json({ ok: true }));
65
- const { port: p } = await app.listen(0);
66
- const url = `http://localhost:${p}`;
67
- // User A (OK)
68
- const r1 = await fetch(url, { headers: { 'x-api-key': 'userA' } });
69
- (0, vitest_1.expect)(r1.status).toBe(200);
70
- // User B (OK - different key)
71
- const r2 = await fetch(url, { headers: { 'x-api-key': 'userB' } });
72
- (0, vitest_1.expect)(r2.status).toBe(200);
73
- // User A Again (Blocked)
74
- const r3 = await fetch(url, { headers: { 'x-api-key': 'userA' } });
75
- (0, vitest_1.expect)(r3.status).toBe(429);
76
- });
77
- });
@@ -1 +0,0 @@
1
- export {};