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 +0,0 @@
1
- export {};
@@ -1,62 +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)('Task engine', () => {
6
- (0, vitest_1.it)('registers and executes a task', async () => {
7
- const app = new index_1.QHTTPX();
8
- const seen = [];
9
- app.task('echo', async (payload) => {
10
- seen.push(payload);
11
- });
12
- await app.enqueue('echo', { hello: 'world' });
13
- (0, vitest_1.expect)(seen).toHaveLength(1);
14
- (0, vitest_1.expect)(seen[0]).toEqual({ hello: 'world' });
15
- });
16
- (0, vitest_1.it)('retries failing tasks according to maxRetries', async () => {
17
- const app = new index_1.QHTTPX();
18
- const spy = vitest_1.vi.fn();
19
- let attempts = 0;
20
- app.task('sometimes-fails', () => {
21
- spy();
22
- attempts += 1;
23
- if (attempts < 3) {
24
- throw new Error('fail');
25
- }
26
- }, { maxRetries: 5 });
27
- await app.enqueue('sometimes-fails', null);
28
- (0, vitest_1.expect)(spy).toHaveBeenCalledTimes(3);
29
- });
30
- (0, vitest_1.it)('propagates errors after exceeding retries', async () => {
31
- const app = new index_1.QHTTPX();
32
- app.task('always-fails', () => {
33
- throw new Error('boom');
34
- }, { maxRetries: 1 });
35
- await (0, vitest_1.expect)(app.enqueue('always-fails', null)).rejects.toThrow('boom');
36
- });
37
- (0, vitest_1.it)('integrates with HTTP route that enqueues a job', async () => {
38
- const app = new index_1.QHTTPX();
39
- const seen = [];
40
- app.task('job', async (payload) => {
41
- seen.push(payload);
42
- });
43
- app.post('/enqueue', async (ctx) => {
44
- const body = ctx.body;
45
- await app.enqueue('job', body.value);
46
- ctx.json({ ok: true });
47
- });
48
- const { port } = await app.listen(0, '127.0.0.1');
49
- const response = await fetch(`http://127.0.0.1:${port}/enqueue`, {
50
- method: 'POST',
51
- headers: {
52
- 'content-type': 'application/json',
53
- },
54
- body: JSON.stringify({ value: 'hello' }),
55
- });
56
- (0, vitest_1.expect)(response.status).toBe(200);
57
- const json = (await response.json());
58
- (0, vitest_1.expect)(json.ok).toBe(true);
59
- (0, vitest_1.expect)(seen).toEqual(['hello']);
60
- await app.close();
61
- });
62
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,47 +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)('Testing utilities', () => {
6
- (0, vitest_1.it)('creates a test client and handles requests', async () => {
7
- const app = new src_1.QHTTPX();
8
- app.get('/hello', (ctx) => {
9
- ctx.json({ message: 'world' });
10
- });
11
- app.post('/echo', (ctx) => {
12
- ctx.json(ctx.body);
13
- });
14
- const client = (0, src_1.createTestClient)(app);
15
- try {
16
- const res1 = await client.get('/hello');
17
- (0, vitest_1.expect)(res1.status).toBe(200);
18
- (0, vitest_1.expect)(await res1.json()).toEqual({ message: 'world' });
19
- const res2 = await client.post('/echo', { foo: 'bar' });
20
- (0, vitest_1.expect)(res2.status).toBe(200);
21
- (0, vitest_1.expect)(await res2.json()).toEqual({ foo: 'bar' });
22
- }
23
- finally {
24
- await client.stop();
25
- }
26
- });
27
- (0, vitest_1.it)('handles custom headers and raw body', async () => {
28
- const app = new src_1.QHTTPX();
29
- app.post('/raw', (ctx) => {
30
- ctx.send(ctx.req.headers['x-custom']);
31
- });
32
- const client = (0, src_1.createTestClient)(app);
33
- try {
34
- const res = await client.post('/raw', 'raw-body', {
35
- headers: {
36
- 'x-custom': 'custom-value',
37
- 'content-type': 'text/plain',
38
- },
39
- });
40
- (0, vitest_1.expect)(res.status).toBe(200);
41
- (0, vitest_1.expect)(await res.text()).toBe('custom-value');
42
- }
43
- finally {
44
- await client.stop();
45
- }
46
- });
47
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,107 +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)('Validation System', () => {
6
- let app;
7
- (0, vitest_1.afterEach)(async () => {
8
- if (app)
9
- await app.close();
10
- });
11
- (0, vitest_1.it)('should validate request body and return 400 on failure', async () => {
12
- app = new server_1.QHTTPX();
13
- const schema = {
14
- body: {
15
- type: 'object',
16
- properties: {
17
- name: { type: 'string', min: 3 },
18
- age: { type: 'number', min: 18 }
19
- }
20
- }
21
- };
22
- app.post('/user', {
23
- schema,
24
- handler: (ctx) => {
25
- ctx.json({ status: 'ok', body: ctx.body });
26
- }
27
- });
28
- const { port } = await app.listen(0);
29
- // Invalid request (age too low)
30
- const res1 = await fetch(`http://localhost:${port}/user`, {
31
- method: 'POST',
32
- headers: { 'Content-Type': 'application/json' },
33
- body: JSON.stringify({ name: 'Alice', age: 10 })
34
- });
35
- (0, vitest_1.expect)(res1.status).toBe(400);
36
- const data1 = await res1.json();
37
- (0, vitest_1.expect)(data1.error).toBe('Validation Error');
38
- (0, vitest_1.expect)(data1.details).toContain('age');
39
- // Valid request
40
- const res2 = await fetch(`http://localhost:${port}/user`, {
41
- method: 'POST',
42
- headers: { 'Content-Type': 'application/json' },
43
- body: JSON.stringify({ name: 'Alice', age: 20 })
44
- });
45
- (0, vitest_1.expect)(res2.status).toBe(200);
46
- const data2 = await res2.json();
47
- (0, vitest_1.expect)(data2.body).toEqual({ name: 'Alice', age: 20 });
48
- });
49
- (0, vitest_1.it)('should validate and coerce query parameters', async () => {
50
- app = new server_1.QHTTPX();
51
- const schema = {
52
- query: {
53
- type: 'object',
54
- properties: {
55
- page: { type: 'number', min: 1 },
56
- sort: { type: 'string', enum: ['asc', 'desc'] }
57
- }
58
- }
59
- };
60
- app.get('/items', {
61
- schema,
62
- handler: (ctx) => {
63
- // ctx.query should have numbers now
64
- ctx.json({
65
- page: ctx.query.page,
66
- pageType: typeof ctx.query.page,
67
- sort: ctx.query.sort
68
- });
69
- }
70
- });
71
- const { port } = await app.listen(0);
72
- // Valid query
73
- const res1 = await fetch(`http://localhost:${port}/items?page=5&sort=desc`);
74
- (0, vitest_1.expect)(res1.status).toBe(200);
75
- const data1 = await res1.json();
76
- (0, vitest_1.expect)(data1.page).toBe(5);
77
- (0, vitest_1.expect)(data1.pageType).toBe('number'); // Coercion worked
78
- (0, vitest_1.expect)(data1.sort).toBe('desc');
79
- // Invalid query
80
- const res2 = await fetch(`http://localhost:${port}/items?page=0&sort=foo`);
81
- (0, vitest_1.expect)(res2.status).toBe(400);
82
- });
83
- (0, vitest_1.it)('should support legacy response schema (no validation)', async () => {
84
- app = new server_1.QHTTPX();
85
- // Legacy schema: just a JSON schema at root
86
- const schema = {
87
- type: 'object',
88
- properties: {
89
- hello: { type: 'string' }
90
- }
91
- };
92
- app.get('/legacy', {
93
- schema,
94
- handler: (ctx) => {
95
- // If validation ran, it might fail because ctx.body is undefined or not matching
96
- // But here we check if it IGNORES validation and uses it for serialization
97
- ctx.json({ hello: 'world', extra: 'hidden' });
98
- }
99
- });
100
- const { port } = await app.listen(0);
101
- const res = await fetch(`http://localhost:${port}/legacy`);
102
- (0, vitest_1.expect)(res.status).toBe(200);
103
- const data = await res.json();
104
- (0, vitest_1.expect)(data.hello).toBe('world');
105
- (0, vitest_1.expect)(data.extra).toBeUndefined(); // fast-json-stringify filters extra fields
106
- });
107
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,146 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- const vitest_1 = require("vitest");
40
- const net = __importStar(require("net"));
41
- const ws_1 = __importDefault(require("ws"));
42
- const src_1 = require("../src");
43
- (0, vitest_1.describe)('WebSocket upgrade', () => {
44
- (0, vitest_1.it)('handles websocket upgrade for registered path', async () => {
45
- const app = new src_1.QHTTPX();
46
- let upgraded = false;
47
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
48
- app.upgrade('/ws', (ws, req) => {
49
- upgraded = true;
50
- ws.close();
51
- });
52
- const { port } = await app.listen(0, '127.0.0.1');
53
- const ws = new ws_1.default(`ws://127.0.0.1:${port}/ws`);
54
- await new Promise((resolve, reject) => {
55
- ws.on('open', () => {
56
- resolve();
57
- });
58
- ws.on('error', (err) => {
59
- reject(err);
60
- });
61
- });
62
- (0, vitest_1.expect)(upgraded).toBe(true);
63
- ws.close();
64
- await app.close();
65
- });
66
- (0, vitest_1.it)('destroys socket when no upgrade handler is registered for path', async () => {
67
- const app = new src_1.QHTTPX();
68
- const { port } = await app.listen(0, '127.0.0.1');
69
- const socket = net.createConnection({ port, host: '127.0.0.1' });
70
- const done = new Promise((resolve) => {
71
- let finished = false;
72
- const finish = () => {
73
- if (!finished) {
74
- finished = true;
75
- resolve();
76
- }
77
- };
78
- socket.on('end', finish);
79
- socket.on('close', finish);
80
- socket.on('error', () => {
81
- finish();
82
- });
83
- setTimeout(finish, 500);
84
- });
85
- const requestLines = [
86
- 'GET /no-ws HTTP/1.1',
87
- 'Host: 127.0.0.1',
88
- 'Upgrade: websocket',
89
- 'Connection: Upgrade',
90
- '',
91
- '',
92
- ];
93
- socket.write(requestLines.join('\r\n'));
94
- await done;
95
- await app.close();
96
- });
97
- (0, vitest_1.it)('supports rooms and broadcasting', async () => {
98
- const app = new src_1.QHTTPX();
99
- app.upgrade('/chat', (ws, req) => {
100
- const url = new URL(req.url || '', 'http://localhost');
101
- const room = url.searchParams.get('room');
102
- if (room) {
103
- ws.join(room);
104
- }
105
- });
106
- app.post('/broadcast', (ctx) => {
107
- const { room, message } = ctx.body;
108
- app.websocket.to(room).emit(message);
109
- ctx.json({ success: true });
110
- });
111
- const { port } = await app.listen(0, '127.0.0.1');
112
- const baseUrl = `ws://127.0.0.1:${port}/chat`;
113
- // Client A in room1
114
- const wsA = new ws_1.default(`${baseUrl}?room=room1`);
115
- const msgsA = [];
116
- wsA.on('message', (data) => msgsA.push(data.toString()));
117
- // Client B in room1
118
- const wsB = new ws_1.default(`${baseUrl}?room=room1`);
119
- const msgsB = [];
120
- wsB.on('message', (data) => msgsB.push(data.toString()));
121
- // Client C in room2
122
- const wsC = new ws_1.default(`${baseUrl}?room=room2`);
123
- const msgsC = [];
124
- wsC.on('message', (data) => msgsC.push(data.toString()));
125
- await Promise.all([
126
- new Promise((resolve) => wsA.on('open', () => resolve())),
127
- new Promise((resolve) => wsB.on('open', () => resolve())),
128
- new Promise((resolve) => wsC.on('open', () => resolve())),
129
- ]);
130
- // Broadcast to room1
131
- // We need to make a POST request.
132
- // Since we are inside test, we can use fetch or http.request.
133
- // Or just use app.websocket directly if we can access it?
134
- // We can access app.websocket directly here since we have app instance.
135
- app.websocket.to('room1').emit('hello room1');
136
- // Wait a bit for messages
137
- await new Promise((resolve) => setTimeout(resolve, 100));
138
- (0, vitest_1.expect)(msgsA).toContain('hello room1');
139
- (0, vitest_1.expect)(msgsB).toContain('hello room1');
140
- (0, vitest_1.expect)(msgsC).not.toContain('hello room1');
141
- wsA.close();
142
- wsB.close();
143
- wsC.close();
144
- await app.close();
145
- });
146
- });
@@ -1,2 +0,0 @@
1
- declare const _default: import("vite").UserConfig;
2
- export default _default;
@@ -1,9 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const config_1 = require("vitest/config");
4
- exports.default = (0, config_1.defineConfig)({
5
- test: {
6
- include: ['tests/**/*.test.ts'],
7
- exclude: ['dist/**', 'node_modules/**'],
8
- },
9
- });
package/docs/AEGIS.md DELETED
@@ -1,91 +0,0 @@
1
- # Aegis Rate Limiter
2
-
3
- **Aegis** is a high-performance, Token Bucket-based rate limiter built directly into QHTTPX. It is designed to protect your application from DDoS attacks, brute-force attempts, and API abuse while maintaining ultra-low latency.
4
-
5
- ## Features
6
-
7
- - **Token Bucket Algorithm**: Smooths out traffic spikes while enforcing strict limits.
8
- - **Zero Dependencies**: Uses an efficient in-memory `Map` by default.
9
- - **Standard Compliance**: Automatically sets `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `Retry-After` headers.
10
- - **Flexible Keys**: Limit by IP, API Key, User ID, or any custom logic.
11
- - **Extensible Storage**: Interface-ready for Redis or other distributed stores.
12
-
13
- ## Usage
14
-
15
- ### Zero-Config (Recommended)
16
-
17
- The easiest way to enable Aegis is via the new fluent API:
18
-
19
- ```typescript
20
- import { app } from 'qhttpx';
21
-
22
- // 1. Presets
23
- app.rateLimit('standard'); // 100 req / 15 min
24
-
25
- // 2. Custom Fluent API
26
- app.allow(500).per('minute');
27
- ```
28
-
29
- ### Manual Middleware
30
-
31
- For specific routes or more control:
32
-
33
- ```typescript
34
- import { app, rateLimit } from 'qhttpx';
35
-
36
- // Global
37
- app.use(rateLimit({
38
- windowMs: 60 * 1000,
39
- max: 100
40
- }));
41
-
42
- // Per Route
43
- app.post('/login', rateLimit({ max: 5 }), async () => { ... });
44
- ```
45
-
46
- ## Proxy Support
47
-
48
- If your app sits behind a reverse proxy (Nginx, AWS ALB, Cloudflare), you must configure **Proxy Trust** so Aegis sees the real user IP instead of the load balancer's IP.
49
-
50
- ```typescript
51
- app.security({
52
- cors: true,
53
- rateLimit: { trustProxy: true }
54
- });
55
- ```
56
-
57
- ## Options
58
-
59
- | Option | Type | Default | Description |
60
- |Params|---|---|---|
61
- | `windowMs` | `number` | `60000` | Time frame for which requests are checked/remembered. |
62
- | `max` | `number` | `100` | Max number of connections during `windowMs` before sending a 429 response. |
63
- | `message` | `string` \| `object` | `"Too many requests..."` | Error message returned to the client. |
64
- | `statusCode` | `number` | `429` | HTTP status code returned. |
65
- | `headers` | `boolean` | `true` | Enable/disable `X-RateLimit-*` headers. |
66
- | `keyGenerator` | `(ctx) => string` | `IP Address` | Function to generate a unique key for the client. |
67
- | `skip` | `(ctx) => boolean` | `undefined` | Function to bypass the limiter. |
68
- | `store` | `RateLimitStore` | `MemoryStore` | Custom storage backend (e.g., Redis). |
69
-
70
- ## Custom Storage (Redis Example)
71
-
72
- You can easily swap the in-memory store for Redis to share limits across a cluster:
73
-
74
- ```typescript
75
- import { RateLimitStore } from 'qhttpx/middleware';
76
-
77
- class RedisStore implements RateLimitStore {
78
- constructor(private redis: RedisClient) {}
79
-
80
- async increment(key: string, windowMs: number) {
81
- // Implement Redis INCR + EXPIRE logic
82
- }
83
-
84
- async decrement(key: string) { ... }
85
- async reset(key: string) { ... }
86
- }
87
-
88
- app.use(rateLimit({
89
- store: new RedisStore(redisClient)
90
- }));
91
- ```