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,10 +0,0 @@
1
- import { QHTTPXContext, QHTTPXMiddleware, SecurityHeadersOptions, SecureDefaultsOptions } from '../core/types';
2
- export { SecurityHeadersOptions, SecureDefaultsOptions };
3
- export declare function createSecurityHeadersMiddleware(options?: SecurityHeadersOptions): QHTTPXMiddleware;
4
- export declare function createSecureDefaults(options?: SecureDefaultsOptions): QHTTPXMiddleware[];
5
- export type RateLimitOptions = {
6
- maxRequests: number;
7
- windowMs: number;
8
- keyGenerator?: (ctx: QHTTPXContext) => string;
9
- };
10
- export declare function createRateLimitMiddleware(options: RateLimitOptions): QHTTPXMiddleware;
@@ -1,74 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSecurityHeadersMiddleware = createSecurityHeadersMiddleware;
4
- exports.createSecureDefaults = createSecureDefaults;
5
- exports.createRateLimitMiddleware = createRateLimitMiddleware;
6
- const cors_1 = require("./cors");
7
- function createSecurityHeadersMiddleware(options = {}) {
8
- const { contentSecurityPolicy = "default-src 'self'", referrerPolicy = 'no-referrer', xFrameOptions = 'SAMEORIGIN', xContentTypeOptions = 'nosniff', xXssProtection = '1; mode=block', strictTransportSecurity, } = options;
9
- return async (ctx, next) => {
10
- if (contentSecurityPolicy) {
11
- ctx.res.setHeader('content-security-policy', contentSecurityPolicy);
12
- }
13
- if (referrerPolicy) {
14
- ctx.res.setHeader('referrer-policy', referrerPolicy);
15
- }
16
- if (xFrameOptions) {
17
- ctx.res.setHeader('x-frame-options', xFrameOptions);
18
- }
19
- if (xContentTypeOptions) {
20
- ctx.res.setHeader('x-content-type-options', xContentTypeOptions);
21
- }
22
- if (xXssProtection) {
23
- ctx.res.setHeader('x-xss-protection', xXssProtection);
24
- }
25
- if (strictTransportSecurity) {
26
- ctx.res.setHeader('strict-transport-security', strictTransportSecurity);
27
- }
28
- await next();
29
- };
30
- }
31
- function createSecureDefaults(options = {}) {
32
- const middlewares = [];
33
- const corsOpts = options.cors;
34
- if (corsOpts !== false) {
35
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
- const opts = (corsOpts === true || corsOpts === undefined) ? {} : corsOpts;
37
- middlewares.push((0, cors_1.createCorsMiddleware)(opts));
38
- }
39
- middlewares.push(createSecurityHeadersMiddleware(options.securityHeaders));
40
- return middlewares;
41
- }
42
- function createRateLimitMiddleware(options) {
43
- const maxRequests = options.maxRequests;
44
- const windowMs = options.windowMs;
45
- const keyGenerator = options.keyGenerator ??
46
- ((ctx) => {
47
- const addr = ctx.req.socket.remoteAddress;
48
- return addr || 'anonymous';
49
- });
50
- const buckets = new Map();
51
- return async (ctx, next) => {
52
- const now = Date.now();
53
- const key = keyGenerator(ctx);
54
- const existing = buckets.get(key);
55
- let bucket = existing;
56
- if (!bucket || bucket.resetAt <= now) {
57
- bucket = {
58
- count: 0,
59
- resetAt: now + windowMs,
60
- };
61
- buckets.set(key, bucket);
62
- }
63
- bucket.count += 1;
64
- if (bucket.count > maxRequests) {
65
- if (!ctx.res.headersSent) {
66
- ctx.res.statusCode = 429;
67
- ctx.res.setHeader('content-type', 'text/plain; charset=utf-8');
68
- }
69
- ctx.res.end('Too Many Requests');
70
- return;
71
- }
72
- await next();
73
- };
74
- }
@@ -1,11 +0,0 @@
1
- import { QHTTPXMiddleware } from '../core/types';
2
- export type StaticOptions = {
3
- root: string;
4
- index?: string;
5
- fallthrough?: boolean;
6
- etag?: boolean;
7
- lastModified?: boolean;
8
- maxAge?: number;
9
- immutable?: boolean;
10
- };
11
- export declare function createStaticMiddleware(options: StaticOptions): QHTTPXMiddleware;
@@ -1,191 +0,0 @@
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
- exports.createStaticMiddleware = createStaticMiddleware;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- function guessContentType(filePath) {
10
- const ext = path_1.default.extname(filePath).toLowerCase();
11
- const types = {
12
- '.html': 'text/html; charset=utf-8',
13
- '.htm': 'text/html; charset=utf-8',
14
- '.js': 'application/javascript; charset=utf-8',
15
- '.mjs': 'application/javascript; charset=utf-8',
16
- '.css': 'text/css; charset=utf-8',
17
- '.json': 'application/json; charset=utf-8',
18
- '.png': 'image/png',
19
- '.jpg': 'image/jpeg',
20
- '.jpeg': 'image/jpeg',
21
- '.gif': 'image/gif',
22
- '.svg': 'image/svg+xml',
23
- '.ico': 'image/x-icon',
24
- '.txt': 'text/plain; charset=utf-8',
25
- '.mp4': 'video/mp4',
26
- '.webm': 'video/webm',
27
- '.mp3': 'audio/mpeg',
28
- '.wav': 'audio/wav',
29
- '.pdf': 'application/pdf',
30
- '.zip': 'application/zip',
31
- };
32
- return types[ext] || 'application/octet-stream';
33
- }
34
- function generateETag(stat) {
35
- const mtime = stat.mtimeMs.toString(16);
36
- const size = stat.size.toString(16);
37
- return `W/"${size}-${mtime}"`;
38
- }
39
- function createStaticMiddleware(options) {
40
- const root = path_1.default.resolve(options.root);
41
- const indexFile = options.index ?? 'index.html';
42
- const fallthrough = options.fallthrough ?? false;
43
- const etag = options.etag ?? true;
44
- const lastModified = options.lastModified ?? true;
45
- const maxAge = options.maxAge ?? 0;
46
- const immutable = options.immutable ?? false;
47
- return async (ctx, next) => {
48
- const method = ctx.req.method || 'GET';
49
- if (method !== 'GET' && method !== 'HEAD') {
50
- if (fallthrough) {
51
- await next();
52
- return;
53
- }
54
- ctx.res.statusCode = 405;
55
- ctx.res.setHeader('Allow', 'GET, HEAD');
56
- ctx.res.setHeader('Content-Type', 'text/plain; charset=utf-8');
57
- ctx.res.end('Method Not Allowed');
58
- return;
59
- }
60
- let requestPath = ctx.url.pathname || '/';
61
- // Prevent directory traversal
62
- requestPath = requestPath.replace(/^(\.\.(\/|\\|$))+/, '');
63
- // Normalize path
64
- let safePath = path_1.default.normalize(requestPath);
65
- if (safePath.startsWith('..'))
66
- safePath = '/'; // Extra safety
67
- // Handle root/directory requests
68
- let filePath = path_1.default.join(root, safePath);
69
- let stat;
70
- try {
71
- stat = await fs_1.default.promises.stat(filePath);
72
- if (stat.isDirectory()) {
73
- filePath = path_1.default.join(filePath, indexFile);
74
- stat = await fs_1.default.promises.stat(filePath);
75
- }
76
- }
77
- catch {
78
- if (fallthrough) {
79
- await next();
80
- return;
81
- }
82
- ctx.res.statusCode = 404;
83
- ctx.res.setHeader('Content-Type', 'text/plain; charset=utf-8');
84
- ctx.res.end('Not Found');
85
- return;
86
- }
87
- if (!stat.isFile()) {
88
- if (fallthrough) {
89
- await next();
90
- return;
91
- }
92
- ctx.res.statusCode = 404;
93
- ctx.res.setHeader('Content-Type', 'text/plain; charset=utf-8');
94
- ctx.res.end('Not Found');
95
- return;
96
- }
97
- // Caching Headers
98
- if (maxAge > 0 || immutable) {
99
- let cacheControl = `public, max-age=${maxAge}`;
100
- if (immutable)
101
- cacheControl += ', immutable';
102
- ctx.res.setHeader('Cache-Control', cacheControl);
103
- }
104
- if (lastModified) {
105
- ctx.res.setHeader('Last-Modified', stat.mtime.toUTCString());
106
- }
107
- if (etag) {
108
- const tag = generateETag(stat);
109
- ctx.res.setHeader('ETag', tag);
110
- // ETag Freshness Check
111
- const ifNoneMatch = ctx.req.headers['if-none-match'];
112
- if (ifNoneMatch === tag) {
113
- ctx.res.statusCode = 304;
114
- ctx.res.end();
115
- return;
116
- }
117
- }
118
- // Last-Modified Freshness Check
119
- const ifModifiedSince = ctx.req.headers['if-modified-since'];
120
- if (ifModifiedSince) {
121
- const since = new Date(ifModifiedSince).getTime();
122
- // Round down mtime to seconds for comparison
123
- const mtime = Math.floor(stat.mtimeMs / 1000) * 1000;
124
- if (mtime <= since) {
125
- ctx.res.statusCode = 304;
126
- ctx.res.end();
127
- return;
128
- }
129
- }
130
- const contentType = guessContentType(filePath);
131
- ctx.res.setHeader('Content-Type', contentType);
132
- ctx.res.setHeader('Accept-Ranges', 'bytes');
133
- // Range Support
134
- const range = ctx.req.headers['range'];
135
- let start = 0;
136
- let end = stat.size - 1;
137
- // let isRange = false;
138
- if (range) {
139
- const parts = range.replace(/bytes=/, '').split('-');
140
- const partialStart = parts[0];
141
- const partialEnd = parts[1];
142
- const parsedStart = parseInt(partialStart, 10);
143
- const parsedEnd = partialEnd ? parseInt(partialEnd, 10) : end;
144
- if (!isNaN(parsedStart) && !isNaN(parsedEnd) && parsedStart <= parsedEnd && parsedEnd < stat.size) {
145
- start = parsedStart;
146
- end = parsedEnd;
147
- // isRange = true;
148
- ctx.res.statusCode = 206;
149
- ctx.res.setHeader('Content-Range', `bytes ${start}-${end}/${stat.size}`);
150
- ctx.res.setHeader('Content-Length', end - start + 1);
151
- }
152
- else {
153
- // Invalid range
154
- ctx.res.statusCode = 416;
155
- ctx.res.setHeader('Content-Range', `bytes */${stat.size}`);
156
- ctx.res.end();
157
- return;
158
- }
159
- }
160
- else {
161
- ctx.res.statusCode = 200;
162
- ctx.res.setHeader('Content-Length', stat.size);
163
- }
164
- if (method === 'HEAD') {
165
- ctx.res.end();
166
- return;
167
- }
168
- const stream = fs_1.default.createReadStream(filePath, { start, end });
169
- // Disable auto-end if we are streaming?
170
- // Actually, we can just pipe. But we need to make sure the server doesn't call res.end()
171
- // The middleware architecture awaits next(), then server calls end().
172
- // If we handle the response here, we should probably set a flag or just return without calling next() (which we do).
173
- // But the server might still try to end it if we don't tell it we are done.
174
- // In QHTTPX, if a handler returns, server checks `res.writableEnded`.
175
- // Pipe calls end() by default.
176
- // We need to wait for the stream to finish before returning from middleware
177
- // so that the server doesn't race.
178
- await new Promise((resolve, reject) => {
179
- stream.pipe(ctx.res);
180
- stream.on('end', resolve);
181
- stream.on('error', (err) => {
182
- stream.destroy();
183
- reject(err);
184
- });
185
- ctx.res.on('close', () => {
186
- stream.destroy();
187
- resolve();
188
- });
189
- });
190
- };
191
- }
@@ -1,19 +0,0 @@
1
- import { Router } from '../router/router';
2
- export type OpenAPIOptions = {
3
- info: {
4
- title: string;
5
- version: string;
6
- description?: string;
7
- };
8
- servers?: {
9
- url: string;
10
- description?: string;
11
- }[];
12
- };
13
- export declare class OpenAPIGenerator {
14
- private router;
15
- private options;
16
- constructor(router: Router, options: OpenAPIOptions);
17
- generate(): object;
18
- private convertSchema;
19
- }
@@ -1,149 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OpenAPIGenerator = void 0;
4
- class OpenAPIGenerator {
5
- constructor(router, options) {
6
- this.router = router;
7
- this.options = options;
8
- }
9
- generate() {
10
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
- const paths = {};
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- const schemas = {};
14
- const methodMap = this.router.getRoutes();
15
- for (const [method, routes] of methodMap.entries()) {
16
- for (const route of routes) {
17
- // Convert /users/:id to /users/{id}
18
- const pathKey = route.path.replace(/:([a-zA-Z0-9_]+)/g, '{$1}');
19
- if (!paths[pathKey]) {
20
- paths[pathKey] = {};
21
- }
22
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
- const operation = {
24
- responses: {}
25
- };
26
- const schema = route.schema;
27
- if (schema) {
28
- // Parameters (Query & Path)
29
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- const parameters = [];
31
- // Path Params
32
- // We can infer path params from the URL segments
33
- const pathParams = route.path.match(/:([a-zA-Z0-9_]+)/g);
34
- if (pathParams) {
35
- pathParams.forEach(p => {
36
- const name = p.substring(1);
37
- // Check if we have specific schema for this param
38
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
- const paramSchema = schema.params?.properties?.[name];
40
- parameters.push({
41
- name,
42
- in: 'path',
43
- required: true,
44
- schema: paramSchema ? this.convertSchema(paramSchema) : { type: 'string' }
45
- });
46
- });
47
- }
48
- // Query Params
49
- if (schema.query) {
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
- const queryProps = schema.query.properties || {};
52
- for (const [key, prop] of Object.entries(queryProps)) {
53
- parameters.push({
54
- name: key,
55
- in: 'query',
56
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
- required: schema.query.required !== false && prop.required !== false, // simplified
58
- schema: this.convertSchema(prop)
59
- });
60
- }
61
- }
62
- if (parameters.length > 0) {
63
- operation.parameters = parameters;
64
- }
65
- // Request Body
66
- if (schema.body) {
67
- operation.requestBody = {
68
- content: {
69
- 'application/json': {
70
- schema: this.convertSchema(schema.body)
71
- }
72
- }
73
- };
74
- }
75
- // Responses
76
- // For now, default to 200 OK
77
- operation.responses['200'] = {
78
- description: 'Successful response',
79
- content: {
80
- 'application/json': {
81
- schema: schema.response ? this.convertSchema(schema.response) : {}
82
- }
83
- }
84
- };
85
- // 400 Bad Request if validation exists
86
- if (schema.body || schema.query || schema.params) {
87
- operation.responses['400'] = {
88
- description: 'Validation Error'
89
- };
90
- }
91
- }
92
- else {
93
- // No schema, generic response
94
- operation.responses['200'] = {
95
- description: 'Successful response'
96
- };
97
- }
98
- paths[pathKey][method.toLowerCase()] = operation;
99
- }
100
- }
101
- return {
102
- openapi: '3.0.0',
103
- info: this.options.info,
104
- servers: this.options.servers,
105
- paths,
106
- components: {
107
- schemas
108
- }
109
- };
110
- }
111
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
- convertSchema(schema) {
113
- if (!schema)
114
- return {};
115
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
- const res = { type: schema.type };
117
- if (schema.properties) {
118
- res.properties = {};
119
- for (const [key, prop] of Object.entries(schema.properties)) {
120
- res.properties[key] = this.convertSchema(prop);
121
- }
122
- }
123
- if (schema.items) {
124
- res.items = this.convertSchema(schema.items);
125
- }
126
- if (schema.min !== undefined) {
127
- if (schema.type === 'string')
128
- res.minLength = schema.min;
129
- if (schema.type === 'number')
130
- res.minimum = schema.min;
131
- if (schema.type === 'array')
132
- res.minItems = schema.min;
133
- }
134
- if (schema.max !== undefined) {
135
- if (schema.type === 'string')
136
- res.maxLength = schema.max;
137
- if (schema.type === 'number')
138
- res.maximum = schema.max;
139
- if (schema.type === 'array')
140
- res.maxItems = schema.max;
141
- }
142
- if (schema.pattern)
143
- res.pattern = schema.pattern;
144
- if (schema.enum)
145
- res.enum = schema.enum;
146
- return res;
147
- }
148
- }
149
- exports.OpenAPIGenerator = OpenAPIGenerator;
@@ -1,18 +0,0 @@
1
- /**
2
- * Radix tree router compiled into a flat array structure for zero-allocation matching.
3
- * Built once at server startup (frozen).
4
- */
5
- import { HTTPMethod, QHTTPXHandler } from '../core/types';
6
- export type CompiledRadixMatch = {
7
- handler: QHTTPXHandler;
8
- params: Record<string, string>;
9
- };
10
- export declare class RadixRouter {
11
- private roots;
12
- private isFrozen;
13
- register(method: HTTPMethod, path: string, handler: QHTTPXHandler): void;
14
- match(method: HTTPMethod, path: string): CompiledRadixMatch | undefined;
15
- freeze(): void;
16
- isFrozenRouter(): boolean;
17
- private normalize;
18
- }
@@ -1,89 +0,0 @@
1
- "use strict";
2
- /**
3
- * Radix tree router compiled into a flat array structure for zero-allocation matching.
4
- * Built once at server startup (frozen).
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.RadixRouter = void 0;
8
- class RadixRouter {
9
- constructor() {
10
- this.roots = new Map();
11
- this.isFrozen = false;
12
- }
13
- register(method, path, handler) {
14
- if (this.isFrozen) {
15
- console.warn(`Radix router is frozen. Late route registration (${method} ${path}) may not be optimized.`);
16
- }
17
- if (!this.roots.has(method)) {
18
- this.roots.set(method, {});
19
- }
20
- const root = this.roots.get(method);
21
- const segments = this.normalize(path);
22
- let node = root;
23
- for (const segment of segments) {
24
- if (!node.children) {
25
- node.children = new Map();
26
- }
27
- const isParam = segment.startsWith(':');
28
- const key = isParam ? segment.slice(1) : segment;
29
- if (!node.children.has(key)) {
30
- node.children.set(key, {
31
- paramKey: isParam ? key : undefined,
32
- });
33
- }
34
- node = node.children.get(key);
35
- }
36
- node.handler = handler;
37
- }
38
- match(method, path) {
39
- const root = this.roots.get(method);
40
- if (!root) {
41
- return undefined;
42
- }
43
- const segments = this.normalize(path);
44
- const params = {};
45
- let node = root;
46
- for (const segment of segments) {
47
- if (!node.children) {
48
- return undefined;
49
- }
50
- // Try exact match first
51
- let child = node.children.get(segment);
52
- // If no exact match, try param match
53
- if (!child) {
54
- for (const [key, candidate] of node.children.entries()) {
55
- if (candidate.paramKey && typeof key === 'string') {
56
- params[key] = segment;
57
- child = candidate;
58
- break;
59
- }
60
- }
61
- }
62
- if (!child) {
63
- return undefined;
64
- }
65
- node = child;
66
- }
67
- if (!node.handler) {
68
- return undefined;
69
- }
70
- return { handler: node.handler, params };
71
- }
72
- freeze() {
73
- if (this.isFrozen) {
74
- return;
75
- }
76
- this.isFrozen = true;
77
- // Future: compile to flat array structure here
78
- }
79
- isFrozenRouter() {
80
- return this.isFrozen;
81
- }
82
- normalize(path) {
83
- if (!path || path === '/') {
84
- return [];
85
- }
86
- return path.split('/').filter((segment) => segment.length > 0);
87
- }
88
- }
89
- exports.RadixRouter = RadixRouter;
@@ -1,21 +0,0 @@
1
- import { QHTTPXHandler, RoutePriority, RouteMetadata } from '../core/types';
2
- export type RouteData = {
3
- handler: QHTTPXHandler;
4
- priority: RoutePriority;
5
- metadata: RouteMetadata;
6
- };
7
- export type MatchResult = {
8
- handler: QHTTPXHandler;
9
- priority: RoutePriority;
10
- params: Record<string, string>;
11
- metadata: RouteMetadata;
12
- };
13
- export declare class RadixTree {
14
- private root;
15
- insert(segments: string[], handler: QHTTPXHandler, priority: RoutePriority, metadata: RouteMetadata): void;
16
- lookup(segments: string[]): MatchResult | null;
17
- lookupPath(path: string): MatchResult | null;
18
- private toMatchResult;
19
- private findPath;
20
- private find;
21
- }